我发现 pthread_barrier_wait 非常慢,所以我的代码中的一个地方用我的屏障版本替换了 pthread_barrier_wait ( my_barrier ),它使用原子变量。我发现它比 pthread_barrier_wait 快得多。使用这种方法有什么缺陷吗?这是对的吗?另外,我不知道为什么它比 pthread_barrier_wait 更快?任何线索?
我主要对线程数量相等的情况感兴趣。
atomic<int> thread_count = 0;
void my_barrier()
{
thread_count++;
while( thread_count % NUM_OF_THREADS )
sched_yield();
}
答案 0 :(得分:8)
您的屏障实施不起作用,至少如果屏障将被多次使用则不行。考虑这种情况:
NUM_OF_THREADS-1
线程在屏障处等待,旋转。此外,处理使用动态分配障碍的一个经常被忽视但令人讨厌的问题是破坏/释放它们。你希望任何一个线程能够在屏障等待返回后执行destroy / free,只要你知道没有人会再次尝试等待它,但这需要确保所有服务员在任何服务员唤醒之前已完成触摸屏障对象的记忆 - 不是一个容易解决的问题。看看我过去关于实施障碍的问题......
How can barriers be destroyable as soon as pthread_barrier_wait returns?
Can a correct fail-safe process-shared barrier be implemented on Linux?
除非你知道你有一个特殊情况,其中没有任何难题适用,不要尝试为你的应用程序实现自己的。
答案 1 :(得分:3)
AFAICT它是正确的,它看起来更快,但在高竞争的情况下它会更糟糕。高强度竞争的情况是,当你有很多线程时,比CPU更多。
有一种方法可以使用事件计数(通过谷歌查看)制作快速障碍。
struct barrier {
atomic<int> count;
struct eventcount ec;
};
void my_barrier_wait(struct barrier *b)
{
eventcount_key_t key;
if (--b->count == 0) {
eventcount_broadcast(&b->ec);
return;
}
for (;;) {
key = eventcount_get(&b->ec);
if (!b->count)
return;
eventcount_wait(&b->ec);
}
}
这应该更好地扩展。
虽然坦率地说,当你使用障碍时,我认为性能不重要,它不应该是一个需要快速的操作,它看起来很像过早的优化。
答案 2 :(得分:2)
只要你不经常使用屏障或你的线号是2的幂,你的屏障就应该是我能看到的。从理论上讲,你的原子会在某处溢出(经过数亿次典型核心数量的使用,但仍然存在),所以你可能想要添加一些功能来重置某个地方。
现在为什么它更快:我不完全确定,但我认为pthread_barrier_wait
会让线程一直睡到醒来的时候。你的条件正在旋转,每次迭代都会产生。但是,如果没有其他应用程序/线程需要处理时间,则可能会在yield
之后直接再次调度线程,因此等待时间会缩短。至少那就是在我的系统中表现出来的那种障碍。
作为旁注:因为你使用atomic<int>
我假设你使用C ++ 11。在这种情况下使用std::this_thread::yield()
而不是sched_yield()
来删除对pthreads的依赖是不是有意义?
This link也可能对你有所帮助,它衡量各种屏障实现的性能(你的情况是lock xadd+while(i<NCPU)
粗略的,除了屈服之外)