私はマルチスレッドに不慣れで、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秒になります。