【发布时间】:2015-04-28 18:00:27
【问题描述】:
我一直在试图弄清楚为什么我的使用蒙特卡罗集成近似 pi 的程序在 pthreads 上运行得比单线程慢得多,两者都是用 C 编写的。我已经在两台不同的机器上测试了这个,它们都运行相同的操作系统,但是不同的硬件,结果几乎相同。
首先是关于我的机器的一些信息:
$ uname -rv
3.19.3-3-ARCH #1 SMP PREEMPT Wed Apr 8 14:10:00 CEST 2015
$ gcc --version
gcc (GCC) 4.9.2 20150304 (prerelease)
$ pacman -Q |grep gcc
gcc-fortran 4.9.2-4
gcc-libs-multilib 4.9.2-4
gcc-multilib 4.9.2-4
lib32-gcc-libs 4.9.2-4
笔记本电脑:Sager NP7358(CPU:i7-4710)
桌面:Franken'puter(CPU:i7-4930k)
起初我遇到了C++ Pthreads - Multithreading slower than single-threading,答案是线程的创建会减慢速度。这对我来说似乎不是问题。单线程程序耗时 3.57 秒,6 线程程序耗时 51 秒,12 线程程序耗时 1 分 6 秒。如果创建线程是唯一的问题,我会除了差异更大。此外,使用 24 个线程需要 1 分 10 秒,尽管这可能是由于线程被重用而不是创建的结果。这些结果适用于我的具有六个内核和超线程的桌面。在我的具有四核和超线程的笔记本电脑上,结果是相似的。
此外,我发现每个线程内完成的工作量增加了一倍,桌面上的执行时间增加了一倍多。然而,在我的笔记本电脑上,时间按预期缩放。也许这是由于架构的差异?艾维布里奇 vs 哈斯韦尔?
根据Htop,正确数量的逻辑核心正在使用中,并且它们被最大化。
我正在使用“gcc -o mcpi_pthread mcpi_pthread.c -pthread”编译所有线程代码,并使用“gcc -o mcpi_nothread mcpi_nothread.c”编译所有单线程代码。你会看到变量 n 和 M。我拥有这两个变量的原因是,起初我不确定它们是否需要相等。事实证明他们这样做了,或者代码段错误。
首先是线程版本。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <pthread.h>
int sum=0;
double frand() //why do I need this?
{
double RandomDouble = (double) rand()/RAND_MAX;
return RandomDouble;
}
int sample ()
/* This program is meant to generate a random x and a random y and check if
* $sqrt{1-x^2}<y$ */
{
double x = frand();
double y = frand();
if( y*y + x*x > 1 )
{
return 0;
}
else
{
return 1;
}
}
void *mcpi_routine(void *args); /*declare the routine, even if you
*/ don't define it
int main ()
/* Now we loop over N sample points to count how many times sample()
* comes up 1 then divide by N to get an approximation of pi/4
*/
{
srand(time(NULL));
long N =8000000 ,M=8 ,n=8;
double pi;
long i;
pthread_t threads[n]; //these are our threads
for(i=0;i<M;i++)
{
pthread_create(&threads[i],NULL,mcpi_routine,(void *) &N);
}
for(i=0;i<M;i++) pthread_join(threads[i],NULL);
pi = (double) 4.0 * sum/ (M*N);
printf("Pi is aproximately equal to %f.4 .\n",pi);
return 0;
}
void *mcpi_routine (void *args ) //need to create a routine
{
int c=0,i;
long *N = (long*) args;
for(i=0;i<*N;i++)
{
c += sample();
}
sum += c;
return 0;
}
现在是单线程
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
//int RAND_MAX = pow(2,16)-1;
double frand() //why do I need this?
{
double RandomDouble = (double) rand()/RAND_MAX;
return RandomDouble;
}
//double frand();
int sample ()
/* This program is meant to generate a random x and a random y and check if
* $sqrt{1-x^2}<y$ */
{
// srand(time(NULL));
double x = frand();
double y = frand();
if( y*y + x*x > 1 )
{
return 0;
}
else
{
return 1;
}
}
main ()
/* Now we loop over N sample points to count how many times sample() comes up 1
* then divide by N to get an aproximation of pi/4 */
{
srand(time(NULL));
int count=0,i;
long N = 6*100000000;
double pi;
for(i=0;i<N;i++)
{
count += sample();
}
pi = 4.0 * count / N;
printf("Pi is aproximately equal to %f.4 .\n",pi);
return 0;
}
我知道两者之间使用的样本点数量不同,因为我正在使用线程版本试图找出它为什么不能正常工作。然而,当我实际比较它们时,我确保线程数乘以每个线程计算的点数对于两者都是相同的。
[编辑] 两周前我最初搜索时没有看到这个帖子,也没有在发布之前再次运行它,但它似乎是完全相同的问题。我在线程的一侧看到了它。 Dividing work to more threads takes more time, why?
答案是 rand() 正在序列化线程,因为它们共享相同的种子或类似的东西。所以这不是线程创建,而是 rand() 函数。我不确定这是否是答案,但我想我应该提一下。
【问题讨论】:
-
与性能问题无关,但在多线程版本中需要锁来保护共享变量
sum(或使用原子增量) -
我也想过这个问题,但是如果两个线程同时尝试写入求和,这只会是一个问题,这不太可能,我没有打扰互斥锁。跨度>
-
'不太可能我没有打扰'......请永远不要在我曾经使用过的任何软件上工作 :) 说真的,这种对编码的态度会让你受伤。做对或花几个月的时间调试不应该发生的事情......
-
如果这是我投入生产的东西,我向你保证我会确保锁定我的变量,但这是一个玩具问题,除非我有线程数,否则它不会返回错误高得离谱。我怀疑这些问题是由于缺乏锁定引起的,因为随着线程越来越多,执行时间越短,问题的可能性就会增加。你是对的,迈克尔,这是一种可怕的态度。懒惰通常是不好的。如果这是为了一份工作,你可能会使用的东西,我不会偷工减料。
标签: c multithreading performance pthreads