gpt4 book ai didi

c - 为什么在我的例子中多线程比顺序编程慢?

转载 作者:太空狗 更新时间:2023-10-29 17:25:17 26 4
gpt4 key购买 nike

我是多线程的新手,尝试通过一个简单的程序来学习它,该程序将 n 加 1 并返回总和。在顺序的情况下,对于 n = 1e5 和 2e5,main 调用 sumFrom1 函数两次;在多线程情况下,使用 pthread_create 创建两个线程,并在单独的线程中计算两个总和。多线程版本比顺序版本慢得多(见下面的结果)。我在 12 CPU 平台上运行它,线程之间没有通信。

多线程:

Thread 1 returns: 0 
Thread 2 returns: 0
sum of 1..10000: 50005000
sum of 1..20000: 200010000
time: 156 seconds

顺序:

sum of 1..10000: 50005000
sum of 1..20000: 200010000
time: 56 seconds

当我在编译中添加-O2时,多线程版本的时间(9s)比顺序版本的时间(11s)少,但没有我预期的多。我总是可以打开 -O2 标志,但我对未优化情况下多线程的低速感到好奇。它应该比顺序版本慢吗?如果没有,我该怎么做才能让它更快?

代码:

#include <stdio.h>
#include <pthread.h>
#include <time.h>

typedef struct my_struct
{
int n;
int sum;
}my_struct_t;

void *sumFrom1(void* sit)
{
my_struct_t* local_sit = (my_struct_t*) sit;
int i;
int nsim = 500000; // Loops for consuming time
int j;

for(j = 0; j < nsim; j++)
{
local_sit->sum = 0;
for(i = 0; i <= local_sit->n; i++)
local_sit->sum += i;
}
}

int main(int argc, char *argv[])
{
pthread_t thread1;
pthread_t thread2;
my_struct_t si1;
my_struct_t si2;
int iret1;
int iret2;
time_t t1;
time_t t2;


si1.n = 10000;
si2.n = 20000;

if(argc == 2 && atoi(argv[1]) == 1) // Use "./prog 1" to test the time of multithreaded version
{
t1 = time(0);
iret1 = pthread_create(&thread1, NULL, sumFrom1, (void*)&si1);
iret2 = pthread_create(&thread2, NULL, sumFrom1, (void*)&si2);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
t2 = time(0);

printf("Thread 1 returns: %d\n",iret1);
printf("Thread 2 returns: %d\n",iret2);
printf("sum of 1..%d: %d\n", si1.n, si1.sum);
printf("sum of 1..%d: %d\n", si2.n, si2.sum);
printf("time: %d seconds", t2 - t1);

}
else // Use "./prog" to test the time of sequential version
{
t1 = time(0);
sumFrom1((void*)&si1);
sumFrom1((void*)&si2);
t2 = time(0);

printf("sum of 1..%d: %d\n", si1.n, si1.sum);
printf("sum of 1..%d: %d\n", si2.n, si2.sum);
printf("time: %d seconds", t2 - t1);
}
return 0;
}

更新 1:

在对“虚假分享”进行了一些谷歌搜索后(感谢@Martin James!),我认为这是主要原因。有(至少)两种方法来修复它:

第一种方法是在两个结构之间插入一个缓冲区(感谢@dasblinkenlight):

my_struct_t  si1;
char memHolder[4096];
my_struct_t si2;

没有-O2,耗时从~156s减少到~38s。

第二种方法是避免频繁更新sit->sum,这可以使用sumFrom1中的临时变量来实现(如@Jens Gustedt 回复):

for(int sum = 0, j = 0; j < nsim; j++)              
{
sum = 0;
for(i = 0; i <= local_sit->n; i++)
sum += i;
}
local_sit->sum = sum;

没有-O2,耗时从~156s减少到~35s或~109s(它有两个峰值!我不知道为什么。)。使用-O2,耗时保持在~8s。

最佳答案

通过修改你的代码

typedef struct my_struct
{
size_t n;
size_t sum;
}my_struct_t;

void *sumFrom1(void* sit)
{
my_struct_t* local_sit = sit;
size_t nsim = 500000; // Loops for consuming time
size_t n = local_sit->n;
size_t sum = 0;
for(size_t j = 0; j < nsim; j++)
{
for(size_t i = 0; i <= n; i++)
sum += i;
}
local_sit->sum = sum;
return 0;
}

现象消失了。您遇到的问题:

  • 使用 int 作为数据类型对于这样的测试是完全错误的。你的总和溢出的数字。签名类型的溢出是未定义的行为。你很幸运,它没有吃你的午餐。
  • 具有间接的边界和求和变量可以为您带来好处额外的加载和存储,在 -O0 的情况下确实完成了这样,带有虚假分享之类的所有含义。

您的代码还发现了其他错误:

  • atoi 缺少包含
  • void* 之间的多余转换
  • time_t 打印为 int

请在发布前使用-Wall 编译您的代码。

关于c - 为什么在我的例子中多线程比顺序编程慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10103127/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com