8

偽共有を実証するために次のタスクがあり、簡単なプログラムを作成しました。

#include <sys/times.h>
#include <time.h>
#include <stdio.h> 
#include <pthread.h> 

long long int tmsBegin1,tmsEnd1,tmsBegin2,tmsEnd2,tmsBegin3,tmsEnd3;

int array[100];

void *heavy_loop(void *param) { 
  int   index = *((int*)param);
  int   i;
  for (i = 0; i < 100000000; i++)
    array[index]+=3;
} 

int main(int argc, char *argv[]) { 
  int       first_elem  = 0;
  int       bad_elem    = 1;
  int       good_elem   = 32;
  long long time1;
  long long time2;
  long long time3;
  pthread_t     thread_1;
  pthread_t     thread_2;

  tmsBegin3 = clock();
  heavy_loop((void*)&first_elem);
  heavy_loop((void*)&bad_elem);
  tmsEnd3 = clock();

  tmsBegin1 = clock();
  pthread_create(&thread_1, NULL, heavy_loop, (void*)&first_elem);
  pthread_create(&thread_2, NULL, heavy_loop, (void*)&bad_elem);
  pthread_join(thread_1, NULL);
  pthread_join(thread_2, NULL);
  tmsEnd1 = clock(); 

  tmsBegin2 = clock();
  pthread_create(&thread_1, NULL, heavy_loop, (void*)&first_elem);
  pthread_create(&thread_2, NULL, heavy_loop, (void*)&good_elem);
  pthread_join(thread_1, NULL);
  pthread_join(thread_2, NULL);
  tmsEnd2 = clock();

  printf("%d %d %d\n", array[first_elem],array[bad_elem],array[good_elem]);
  time1 = (tmsEnd1-tmsBegin1)*1000/CLOCKS_PER_SEC;
  time2 = (tmsEnd2-tmsBegin2)*1000/CLOCKS_PER_SEC;
  time3 = (tmsEnd3-tmsBegin3)*1000/CLOCKS_PER_SEC;
  printf("%lld ms\n", time1);
  printf("%lld ms\n", time2);
  printf("%lld ms\n", time3);

  return 0; 
} 

結果を見て非常に驚きました(i5-430Mプロセッサで実行しました)。

  • 偽共有の場合、1020ミリ秒でした。
  • 偽共有がなければ、それは710ミリ秒で、300%ではなく30%しか高速ではありませんでした(一部のサイトでは、300〜400%より高速になると書かれていました)。
  • pthreadを使用しない場合、580ミリ秒でした。

私の間違いを見せてください、またはそれが起こる理由を説明してください。

4

2 に答える 2

23

偽共有は、物理メモリの同じ領域にアクセスする別々のキャッシュを持つ複数のコアの結果です(ただし、同じアドレスではありません-それは真の共有になります)。

偽共有を理解するには、キャッシュを理解する必要があります。ほとんどのプロセッサでは、各コアに独自のL1キャッシュがあり、最近アクセスしたデータを保持します。キャッシュは「行」で編成され、データのチャンクが整列されます。通常、長さは32バイトまたは64バイトです(プロセッサによって異なります)。キャッシュにないアドレスから読み取る場合、行全体がメインメモリ(またはL2キャッシュ)からL1に読み込まれます。キャッシュ内のアドレスに書き込むと、そのアドレスを含む行は「ダーティ」とマークされます。

ここで共有の側面が出てきます。複数のコアが同じ行から読み取っている場合、それぞれがL1の行のコピーを持つことができます。ただし、コピーにダーティのマークが付けられている場合は、他のキャッシュの行が無効になります。これが行われなかった場合、1つのコアで行われた書き込みは、かなり後になるまで他のコアに表示されない可能性があります。そのため、次に他のコアがその行から読み取りを行うと、キャッシュが失われ、その行を再度フェッチする必要があります。

コアが同じ行の異なるアドレスに対して読み取りと書き込みを行っている場合、偽共有が発生します。データを共有していなくても、キャッシュは非常に近いため、キャッシュは同じように機能します。

この効果は、プロセッサのアーキテクチャに大きく依存します。シングルコアプロセッサを使用している場合、共有が行われないため、効果はまったく見られません。キャッシュラインが長ければ、「悪い」場合と「良い」場合の両方で効果が見られます。これは、それらがまだ接近しているためです。コアがL2キャッシュを共有していなかった場合(私はそれらが共有していると思います)、キャッシュミスでメインメモリに到達する必要があるため、あなたが言ったように300〜400%の違いが見られるかもしれません。

また、各スレッドが読み取りと書き込みの両方であることが重要であることを知りたいと思うかもしれません(=ではなく+ =)。一部のプロセッサにはライトスルーキャッシュがあります。つまり、コアがキャッシュにないアドレスに書き込む場合、メモリから行を見逃してフェッチすることはありません。これを、書き込みを見逃すライトバックキャッシュと比較してください。

于 2011-11-30T19:10:34.743 に答える
4

Cのclock()関数の概要:開始から終了までに経過したCPUクロックの数を示します。したがって、2つの並列スレッドを実行する場合、CPUサイクル数はCPU1のクロックサイクル+CPU2のクロックサイクルになります。

あなたが欲しいのは本物のタイマー時計だと思います。この使用のために

clock_gettime()

期待どおりの出力が得られるはずです。

私はclock_gettime()であなたのコードを実行しました、そして私はこれを手に入れました:

  • 偽共有あり874.587381ミリ秒
  • 偽共有なし331.844278ミリ秒
  • シーケンシャル計算604.160276ミリ秒
于 2017-03-18T18:06:02.097 に答える