【问题标题】:how to implement a "soft barrier" in multithreaded c++如何在多线程 C++ 中实现“软屏障”
【发布时间】:2011-12-28 20:19:51
【问题描述】:

我有一些具有以下结构的多线程 c++ 代码:

do_thread_specific_work();
update_shared_variables();
//checkpoint A
do_thread_specific_work_not_modifying_shared_variables();
//checkpoint B
do_thread_specific_work_requiring_all_threads_have_updated_shared_variables();

检查点 B 之后的工作是在所有线程仅到达检查点 A 时开始的工作,因此我提出了“软屏障”的概念。

通常,多线程库仅提供“硬屏障”,其中所有线程必须达到某个点才能继续。显然可以在检查点 B 使用硬屏障。

使用软屏障可以缩短执行时间,特别是因为检查点 A 和 B 之间的工作可能没有在线程之间进行负载平衡(即 1 个到达检查点 A 但未到达检查点 B 的慢线程可能导致所有其他人在检查站 B) 前的屏障处等待。

我尝试过使用原子来同步事物,并且我 100% 确定它不能保证工作。例如使用 openmp 语法,在并行部分开始之前:

shared_thread_counter = num_threads;  //known at compile time
#pragma omp flush

然后在检查点A:

#pragma omp atomic
shared_thread_counter--;

然后在检查点 B(使用轮询):

#pragma omp flush
while (shared_thread_counter > 0) {
  usleep(1);  //can be removed, but better to limit memory bandwidth
  #pragma omp flush
}

我设计了一些实验,在这些实验中我使用原子来指示某些操作在完成之前完成。该实验大部分时间都适用于 2 个线程,但当我有很多线程(如 20 或 30)时始终失败。我怀疑这是因为现代 CPU 的缓存结构。即使一个线程在执行原子减量之前更新了某个其他值,也不能保证另一个线程以该顺序读取它。考虑另一个值是缓存未命中并且原子减量是缓存命中的情况。

回到我的问题,如何正确实施这个“软屏障”?是否有任何内置功能可以保证此类功能?我更喜欢 openmp,但我熟悉大多数其他常见的多线程库。

现在作为一种解决方法,我在检查点 B 处使用了硬屏障,并且我已经重组了我的代码以使检查点 A 和 B 之间的工作自动在线程之间进行负载平衡(这有时相当困难)。

感谢您的任何建议/见解:)

【问题讨论】:

  • 我对你的问题感到困惑。如果你在 A 处有一个硬屏障,那么 A 之后的任何代码,包括 B 之后的代码,都只会在每个线程通过该屏障之后执行。你在 B 需要什么障碍?
  • @Walter 在 A 处使用硬屏障在功能上是正确的,但可能会失去性能。考虑在允许任何线程开始 A 和 B 之间的工作之前等待一个慢线程到达 A 的惩罚(因为基于结构我们真的不需要在 A 等待)。

标签: c++ multithreading openmp


【解决方案1】:

使用条件变量怎么样?我不确定是否提供了条件变量,因为我不熟悉 OpenMP。

int counter = 0;
condition_variable cond;

// checkpoint A
++counter;
cond.notify_all();

// checkpoint B
cond.wait_until( counter >= NUM_THREADS );

在每个线程到达检查点A之前,没有线程可以通过检查点B。

【讨论】:

  • 哪些库支持条件变量?这是否保证不会出现像我给出的原子解决方案那样的竞争条件?
  • @Jason boost::thread 和 std::thread in C++11 可能还有 tbb。 ideone.com/avjlp 是我刚写的粗略代码。希望对你有帮助。
  • 感谢您提出条件变量的建议,但是经过一些研究,我认为它不能保证没有可能的竞争条件。据我了解,避免竞速的唯一方法是由于缓存未命中(例如外部内存访问)导致的最坏情况延迟仍然比线程之间的同步机制(例如锁,或您建议的条件变量)更快.显然,这将取决于所述同步结构、硬件支持和许多其他参数的实现。 (续...)
  • 具有讽刺意味的是,这个软屏障的全部目的是加快计算速度,但为了保证正确性,必须使用慢速同步结构。我认为真正的解决方案是重构代码,使其具有更好的负载平衡特性。无论如何,再次感谢:)
猜你喜欢
  • 2014-12-01
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 2014-08-04
  • 1970-01-01
  • 2021-05-14
  • 1970-01-01
相关资源
最近更新 更多