Ложное разделение между потоками происходит, когда 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();
}
Потоки в первом блоке страдают от ложного совместного использования.Потоки во втором блоке не (спасибо 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;
};
Это предотвращает все проблемы с совместным использованием.