Я пытаюсь реализовать барьер вращающегося потока, используя атомику, в частности, __sync_fetch_and_add. https://gcc.gnu.org/onlinedocs/gcc-4.4.5/gcc/Atomic-Builtins.html
Я в основном хочу альтернативу барьеру pthread. Я использую Ubuntu в системе, которая может работать около сотни потоков параллельно.
int bar = 0; //global variable
int P = MAX_THREADS; //number of threads
__sync_fetch_and_add(&bar,1); //each thread comes and adds atomically
while(bar<P){} //threads spin until bar increments to P
bar=0; //a thread sets bar=0 to be used in the next spinning barrier
Это не работает по очевидным причинам (поток может установить bar = 0, а другой поток застрянет в бесконечном цикле while и т. Д.). Я видел реализацию здесь: написание (вращающегося) барьера для потоков с использованием атомарности c ++ 11, однако это кажется слишком сложным, и я думаю, что его производительность может быть хуже, чем барьер pthread.
Ожидается, что эта реализация также будет генерировать больше трафика в иерархии памяти из-за того, что строка кэша бара пинг-пинг-понг между потоками.
Любые идеи о том, как использовать эти атомарные инструкции, чтобы сделать простой барьер? Оптимальная коммуникационная схема также была бы полезна дополнительно.
Вместо того, чтобы крутиться на счетчик потоков, лучше крутиться количество прошедших барри, который будет увеличен только последним потоком, столкнувшимся с барьером. Таким образом, вы также уменьшаете нагрузку на кэш-память, поскольку вращающаяся переменная теперь обновляется только одним потоком.
int P = MAX_THREADS;
int bar = 0; // Counter of threads, faced barrier.
volatile int passed = 0; // Number of barriers, passed by all threads.
void barrier_wait()
{
int passed_old = passed; // Should be evaluated before incrementing *bar*!
if(__sync_fetch_and_add(&bar,1) == (P - 1))
{
// The last thread, faced barrier.
bar = 0;
// *bar* should be reseted strictly before updating of barriers counter.
__sync_synchronize();
passed++; // Mark barrier as passed.
}
else
{
// Not the last thread. Wait others.
while(passed == passed_old) {};
// Need to synchronize cache with other threads, passed barrier.
__sync_synchronize();
}
}
Обратите внимание, что вам нужно использовать volatile
модификатор для вращающейся переменной.
C ++ код может быть несколько быстрее, чем C один, так как он может использовать приобретать/релиз барьеры памяти вместо полный один, который является единственным барьером, доступным из __sync
функции:
int P = MAX_THREADS;
std::atomic<int> bar = 0; // Counter of threads, faced barrier.
std::atomic<int> passed = 0; // Number of barriers, passed by all threads.
void barrier_wait()
{
int passed_old = passed.load(std::memory_order_relaxed);
if(bar.fetch_add(1) == (P - 1))
{
// The last thread, faced barrier.
bar = 0;
// Synchronize and store in one operation.
passed.store(passed_old + 1, std::memory_order_release);
}
else
{
// Not the last thread. Wait others.
while(passed.load(std::memory_order_relaxed) == passed_old) {};
// Need to synchronize cache with other threads, passed barrier.
std::atomic_thread_fence(std::memory_order_acquire);
}
}
Других решений пока нет …