使用Atomic Builtins旋转螺纹护栏

时间:2015-11-08 20:29:07

标签: c++ multithreading atomic compare-and-swap barrier

我试图使用原子实现旋转线程障碍,特别是__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 atomics编写一个(旋转)线程障碍,但它似乎太复杂了,我认为它的性能可能比pthread障碍更差。

由于bar的缓存行在线程之间是ping的,因此预计此实现也会在内存层次结构中产生更多流量。

关于如何使用这些原子指令制作简单屏障的任何想法?通信最优方案也会有所帮助。

1 个答案:

答案 0 :(得分:2)

不是旋转线程的计数器,而是最好旋转传递的数量,这将仅由最后一个线程递增,面临障碍。这样你也可以减少内存缓存压力,因为旋转变量现在只能由单线程更新。

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);
    }
}