6

我是多线程的新手,并尝试通过一个简单的程序来学习它,它将 1 添加到 n 并返回总和。在顺序情况下,对于 n = 1e5 和 2e5main调用该函数两次;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,耗时约 8 秒。

4

1 回答 1

3

通过将您的代码修改为

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_tas的打印int

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

于 2012-04-11T10:26:33.827 回答