6

私はマルチスレッドに不慣れで、nに1を加算して合計を返す単純なプログラムを通してそれを学ぼうとしています。シーケンシャルの場合、n=1e5と2e5に対して関数を2回main呼び出します。sumFrom1マルチスレッドの場合、を使用して2つのスレッドが作成されpthread_create、2つの合計が別々のスレッドで計算されます。マルチスレッドバージョンは、シーケンシャルバージョンよりもはるかに低速です(以下の結果を参照)。これを12CPUプラットフォームで実行しましたが、スレッド間の通信がありません。

マルチスレッド:

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を追加すると、マルチスレッドバージョン(9秒)の時間はシーケンシャルバージョン(11秒)の時間よりも短くなりますが、期待したほどではありません。いつでも-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;                                                                                         
}   

UPDATE1:

「偽共有」について少し調べた後(ありがとう、@ Martin James!)、それが主な原因だと思います。それを修正するには(少なくとも)2つの方法があります。

最初の方法は、2つの構造体の間にバッファーゾーンを挿入することです(ありがとう、@ dasblinkenlight):

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

-O2がない場合、時間は約156秒から約38秒に短縮されます。

2番目の方法は、頻繁な更新を回避することです。これは、 (@ Jens Gustedtが返信したように)sit->sum一時変数を使用して実現できます。sumFrom1

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がないと、時間がかかる時間は約156秒から約35秒または約109秒に減少します(2つのピークがあります!理由はわかりません)。-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_tとしての印刷int

-Wall投稿する前に、でコードをコンパイルしてください。

于 2012-04-11T10:26:33.827 に答える