スレッド間の偽共有は、2 つ以上のスレッドが同じキャッシュ ラインを使用する場合です。
例:
struct Work {
Work( int& d) : data( d ) {}
void operator()() {
++data;
}
int& data;
};
int main() {
int false_sharing[10] = { 0 };
boost::thread_group threads;
for (int i = 0; i < 10; ++i)
threads.create_thread(Work(false_sharing[i]));
threads.join_all();
int no_false_sharing[10 * CACHELINE_SIZE_INTS] = { 0 };
for (int i = 0; i < 10; ++i)
threads.create_thread(Work(no_false_sharing[i * CACHELINE_SIZE_INTS]));
threads.join_all();
}
最初のブロックのスレッドは、偽共有の影響を受けます。2 番目のブロックのスレッドはそうではありません (おかげでCACHELINE_SIZE
)。
スタック上のデータは、常に他のスレッドから「遠く」離れています。(たとえば、ウィンドウの下で、少なくとも数ページ)。
Work
関数オブジェクトの定義では、 のインスタンスがヒープ上に作成され、このヒープ領域がスレッド内で使用されるため、誤った共有が発生する可能性があります。
これにより、複数のWork
インスタンスが隣接する可能性があり、キャッシュ ラインの共有が発生する可能性があります。
しかし...あなたのサンプルは意味がありません。なぜなら、データは外部で決して触れられないため、誤った共有が不必要に誘発されるからです。
このような問題を防ぐ最も簡単な方法は、「共有」データをスタックにローカルにコピーしてから、スタック コピーで作業することです。作業が終了したら、それを出力変数にコピーします。
例えば:
struct Work {
Work( int& d) : data( d ) {}
void operator()()
{
int tmp = data;
for( int i = 0; i < lengthy_op; ++i )
++tmp;
data = tmp;
}
int& data;
};
これにより、共有に関するすべての問題が回避されます。