簡體   English   中英

為什么在我的例子中多線程比順序編程慢?

[英]Why is multithreading slower than sequential programming in my case?

我是多線程的新手,並嘗試通過一個簡單的程序來學習它,該程序將 1 添加到 n 並返回總和。 在順序情況下,對於 n = 1e5 和 2e5, main兩次調用sumFrom1 function; 在多線程情況下,使用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 秒左右。

通過將代碼修改為

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編譯您的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM