【问题标题】:Parallelizing std::replace on std::deque在 std::deque 上并行化 std::replace
【发布时间】:2017-12-19 04:49:45
【问题描述】:

首先我知道一个双端队列上的多个写入器不是很容易处理。但是通过以下算法,我可以保证元素上没有并发访问。该算法将一个双端队列(它非常大,这就是我并行化它的原因)分成块,并且 std::replaces 替换双端队列中的一个值。问题是,在某些情况下,在替换任意值之后,该值似乎仍然存在(顺便说一句:新值与旧值不同的情况并非如此)。是否可能该值未从 cpu 寄存器同步到内存?代码如下:

std::deque<int*> _deque;
...
int threadsCount = 25;          
int chunkSize = ceil((float) _deque.size() / (float) threadsCount);                                                                                                                          
std::vector<std::thread> threads;
for (int threadNo = 0; threadNo < threadsCount; threadNo++) {
   std::uint64_t beginIndex = threadNo * chunkSize;
   std::uint64_t endIndex = (threadNo + 1) * chunkSize;
   if (endIndex > _deque.size()) {    
      endIndex = _deque.size();      
   }
   std::deque<int*>::iterator beginIterator = _deque.begin() + beginIndex;
   std::deque<int*>::iterator endIterator = _deque.begin() + endIndex;
   threads.push_back(std::thread([beginIterator, endIterator, elementToReplace, elementNew] () {
      std::replace(beginIterator, endIterator, elementToReplace, elementNew);                                      
   }));
}
for (int threadNo = 0; threadNo < threadsCount; threadNo++) {                                                                                                                               
   threads[threadNo].join();     
}

在该算法之后,有时(非确定性)被替换的 (elementToReplace) 值仍在双端队列中。

【问题讨论】:

  • 仅供参考:以__ 开头的标识符是保留的,您不应使用它们。
  • beginend 都是非常量成员函数,它们被同时调用,这是一个数据竞争(由 deque 的接口控制),无论迭代器是否指向到不同的对象
  • @Rakete1111:实际上所有包含 __的标识符都保留给实现用于所有目的。以_ 开头后跟大写字母的标识符也是如此。
  • @PasserBy 你在技术上当然是正确的,但你能解释一下为什么在这种情况下这可能会导致问题吗?
  • @PasserBy begin/end 需要具有 O(1) 复杂度,因此它们可能只返回一个值。这怎么会导致数据竞争,因为我认为通常只有并发读取是安全的?

标签: c++ memory parallel-processing deque


【解决方案1】:

不用手动实现这样的算法,只需传递适当的执行策略:

std::replace(std::execution::par, deque.begin(), deque.end(), elementToReplace, elementNew);
//           ^^^^^^^^^^^^^^^^^^^
//     executes the algorithm in parallel

请注意,您必须使用 C++17 或更高版本进行编译。

【讨论】:

  • 有趣!还不知道
  • 该死,我的环境还不支持
【解决方案2】:

它看起来像一个竞争条件,但我无法重现它:http://cpp.sh/5egzm 这可能取决于您正在使用的双端队列实现,但它看起来很奇怪

【讨论】:

  • 算法运行了几天没有任何问题,但后来它发生了;(现在看来,没有并行化它可以正常工作。
  • 是的,这就是我的意思,这是竞争条件的明显标志。看起来有些东西正在让你的双端队列重新索引自己
  • 是的,看起来是这样,但对我来说很奇怪。
  • @IlBeldux 重新索引是什么意思。这是我第一次听说双端队列中的重新索引过程
  • 并且:在当前的实现中,我不再使用向量了。
【解决方案3】:

仅供参考:由于上述算法崩溃并且建议的执行策略在我的系统上仍然不可用,我使用了 GNU 并行:

__gnu_parallel::replace(_deque.begin(), _deque.end(), elementToReplace, elementNew);

我会告诉你它是否有效以及性能统计数据。

【讨论】:

  • 性能不如我的算法好,但它确实有效。我自己假设的算法不起作用。
  • 这个解决方案也不起作用。一段时间后,比赛条件再次发生。 ;(
  • 正常的 std::replace 现在应该可以与并行工作具有相同的性能
  • 我在这里认识到的问题是,如果 RAM 已满(包括磁盘缓存),gnu 并行框架将无法正常工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-15
  • 2023-03-14
  • 2019-04-09
  • 2012-03-07
  • 1970-01-01
  • 1970-01-01
  • 2015-01-05
相关资源
最近更新 更多