-1

複数のプロセスによって並行して実行される次のコードを作成しました。

// spawn 10 times with id=0..9 by a master process.
void slave_processing(int id)
{
  SHARED_TYPE last=id;
   for(;;) {
     /* each slave operates on a specific byte of the shared array. */
     if (shared[id]!=last) fprintf(stderr,"S%i expected %i\n",id,last);
     shared[id]+=id;
     last+=id;
     if (last>10*id) {last=0; shared[id]=0;}
   }
}

それらはすべて同じ (IPC/linux) 共有メモリを使用していますが、それぞれが配列の個別のエントリにアクセスしています。と の両方の場合、デュアルコア マシンで問題なく動作しSHARED_TYPEます。アグレッシブな最適化 (-O3) でコンパイルし、アセンブルされたバイナリをチェックして、メモリ参照がアクセスに対して実行され、レジスタが使用されていることを確認しました。intcharshared[id]last

それでも、私は困惑しています。ある時点で、あるコアからのバイトへの影響が、他のコアからのキャッシュされたコンテンツに反映されない可能性があると予想していました。たとえば、あるコアがキャッシュに [xxyyzzuu] を持ち、[xxyyZZuu] をメモリに書き戻すと同時に、別のコアがメモリ ワードを [XXyyzzuu] にアップグレードした可能性があります (char の各ペアが 32 ビット ワードのバイトであると仮定します)。 .

shmgetで取得したメモリをキャッシュできないように、Linux は何らかの魔法の設定を行っていますか? または、コア #1 がコア #2 の最新の変更を読み取って、厄介な副作用なしで選択したバイトを更新できるようにする、低レベルのキャッシュ同期が行われていますか?

上記のコードが失敗した (fprintf に入った) 場所を知っている他の (マルチコア) アーキテクチャはありますか?

4

1 に答える 1

2

コードは、コンパイラがループ内で干渉する値をキャッシュしない (たとえば、メモリからレジスタにコンテンツを一度読み取り、メモリの代わりにレジスタを使用し続ける) ことを前提としていますが、共有セグメント。すべてのパーツがメモリに書き込まれたデータを確認できるようにするには、shared配列へのアクセスはメモリ バリアを使用して行う必要があります。

コンパイラに依存しますsharedが、揮発性の場合は通常メモリフェッチを行います。

fprintf() を呼び出すため、コンパイラは fprintf() が配列内の何かにアクセスまたは更新できると想定する必要がありますshared。結局のところ、コンパイラは fprintf() が何をするかを知らないため、理論的にはグローバル配列にアクセスできます。コンパイラはそのメモリを再取得する必要がありますshared(fprintf で更新できないことが証明されない限り)

shared揮発性でない場合、コードは異なる動作をする可能性があります。

SHARED_TYPE last=id;
int wrong = 0,i;
for(i = 0; i < 10000000;i++) {
  if (shared[id]!=last) wrong++;
  shared[id]+=id;
  last+=id;
  if (last>10*id) {last=0; shared[id]=0;}
}
return wrong;

ここで、コンパイラはshared[id]、各反復での最新の値を取得するためにメモリ フェッチを行う必要はありません。次に、コンパイラが値をキャッシュする場合、ループをスピンするだけで、メモリから更新された値を取得せずに同じ正しいことを何度もチェックし、wrong期待どおり = 0 のままにします。ここで行うことはすべて安全ではなく、コンパイラが生成するコードによって異なります。小さなコードの変更に対して、実質的に異なる結果のコードが生成される可能性があります。

これと絡み合っているのは、メモリからアトミックに読み取ることができるデータ型です。これは、プロセッサ、データ型、およびアクセスがメモリで整列されているかどうかによって異なります。

これは、キャッシュの一貫性に追加されます。私たちが使用する一般的なデスクトップおよびサーバー プラットフォームはキャッシュ コヒーレントです。つまり、ハードウェアがメモリ更新の魔法を処理するため、変更はすべてのプロセッサ/コアで認識されます。たとえば、別のコアが必要なときに 1 つのコアのみの L1 キャッシュに常駐することはありません。同じメモリ位置にアクセスします。(DMA に使用される領域など、一部のメモリ領域では、そのようなキャッシュの一貫性が保証されない場合があります)

今日のデスクトップ/サーバー プラットフォームは NUMA によく似ていますが、より大規模で特別に構築された NUMA システムの多くは、キャッシュの一貫性を保証していません。

于 2012-11-30T15:25:54.897 に答える