C++ 在std::deque上并行化std::replace

C++ 在std::deque上并行化std::replace,c++,memory,parallel-processing,deque,C++,Memory,Parallel Processing,Deque,首先,我知道在一个deque上有多个writer并不容易处理。但是使用下面的算法,我可以保证对元素没有并发访问。该算法将deque(它非常大,这就是我将其并行化的原因)分成块,std::替换deque中的一个值。问题是,在某些情况下,在替换任意值后,该值似乎仍然存在(顺便说一句:新值与旧值不同)。是否有可能值没有从cpu寄存器同步到内存?代码如下: std::deque<int*> _deque; ... int threadsCount = 25; int ch

首先,我知道在一个deque上有多个writer并不容易处理。但是使用下面的算法,我可以保证对元素没有并发访问。该算法将deque(它非常大,这就是我将其并行化的原因)分成块,std::替换deque中的一个值。问题是,在某些情况下,在替换任意值后,该值似乎仍然存在(顺便说一句:新值与旧值不同)。是否有可能值没有从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();     
}
std::deque\u deque;
...
int-threadscont=25;
int chunkSize=ceil((float)_deque.size()/(float)threadscont);
向量线程;
对于(int-threadNo=0;threadNo\u deque.size()){
endIndex=_deque.size();
}
std::deque::迭代器beginIterator=\u deque.begin()+beginIndex;
std::deque::iterator endIterator=\u deque.begin()+endIndex;
threads.push_back(std::thread([beginIterator,endIterator,elementToReplace,elementNew])(){
std::replace(beginIterator、endIterator、elementToReplace、elementNew);
}));
}
对于(int-threadNo=0;threadNo

在该算法之后,有时(不确定)被替换的(elementToReplace)值仍在数据块中。

只需传递适当的执行策略,而不是手动实现该算法:

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

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

它看起来像一个竞争条件,但我无法复制它:
这可能是由于您正在使用的deque实现,但它看起来很奇怪,仅供参考:由于上述算法崩溃,并且建议的执行策略在我的系统上仍然不可用,我使用了GNU并行:

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

我将告诉您它是否有效以及性能统计信息。

FYI:以
\uuuu
开头的标识符是保留的,您不应该使用它们。
begin
end
都是非常量成员函数,它们是并发调用的,这是一个数据竞争(由
deque
的接口控制)不管迭代器是否指向不同的objects@Rakete1111:实际上,所有包含
的标识符都保留给实现,用于所有目的。标识符也一样,以
\uu
开头,后跟大写字母。@PasserBy您在技术上当然是正确的,但您能解释一下为什么在这种情况下这可能会导致问题吗?@PasserBy
begin
/
end
要求具有O(1)复杂度,因此它们可能只返回一个值。这将如何导致数据竞争,因为我认为通常只有并发读取是安全的?有趣!不知道yetDamn,在我的环境中还不受支持该算法运行了几天没有任何问题,但后来它发生了;(现在看来,没有并行化,它工作起来没有任何问题。是的,这就是我的意思,这是一个种族状况的明显迹象。看起来有什么东西在让你的deque重新编制索引。是的,看起来是这样,但对我来说很奇怪。@IlBeldux,重新编制索引是什么意思。这是我第一次听说重新编制索引的过程在当前的实现中,我使用了一个向量no-deque。性能不如我的算法好,但它可以工作。我假设的算法不起作用。这个解决方案也不起作用。一段时间后,竞争条件再次发生;(普通std::replace现在应该以与并行工作相同的性能工作。我在这里认识到的问题是,如果RAM已满(包括磁盘缓存),gnu并行框架将无法正常工作。