并行化std::map迭代
openMp : parallelize std::map iteration
有一些关于这个问题的帖子,但没有一个让我满意。我没有openMp 3.0的支持,我需要在映射上并行化迭代。我想知道这个解决方案是否有效:
auto element = myMap.begin();
#pragma omp parallel for shared(element)
for(int i = 0 ; i < myMap.size() ; ++i){
MyKeyObject * current_first = nullptr;
MyValueObject * current_second = nullptr;
#pragma omp critical
{
current_first = element->first;
current_second = element->second;
++element;
}
// Here I can use 'current' as in a usual loop
}
所以我使用for循环只是为了确保线程将处理相同数量的map元素。这是一个正确的猜测还是会失败?
ps:我正在visual studio 2012上工作,所以如果你有一个关于如何使我的编译器支持openMp 3.0的提示,那也会解决我的问题。
这不是对你问题的直接回答,但我会尽量为你节省一些未来糟糕的"OpenMP with Visual Studio"体验。
Microsoft C/c++ Compiler只支持OpenMP 2.0。没有办法让它支持OpenMP 3.0或更高版本,因为OpenMP是内置在编译器核心中的,而不是一个附加包(除非有人提出一个外部的源码到源码转换引擎),而且微软似乎对提供进一步的OpenMP支持不感兴趣,同时推出他们自己的解决方案(见下文)。因此,您应该使用与Visual Studio集成的Intel C/c++编译器,或者像GCC或PGI C/c++编译器这样的独立编译器。
如果你是专门为Windows开发的,那么你可能想要放弃OpenMP而使用并发运行时,特别是PPL。PPL随Visual Studio 2012及更新版本一起提供,并提供与STL中的一些算法等价的数据和任务并行。你感兴趣的是concurrency::parallel_for_each()
,它是std::for_each()
的平行版本。它适用于前向迭代器,尽管效率不如随机迭代器。但是你必须确保处理映射的一个元素至少需要一千条指令,否则并行化就不会有好处。
如果您的目标是跨平台兼容性,那么英特尔线程构建块(简称英特尔TBB)是PPL的替代方案。它提供了tbb::parallel_do()
算法,该算法专门设计用于前向迭代器。对于每个map元素的工作量,同样的警告也适用。
您的方法将工作,因为您访问并迭代临界区中的共享对象element
。这是否有利于性能,您将不得不进行测试。以下是您可能想要考虑的另一种方法。我把它叫做"快进"方法。
让我们假设你想并行执行
for(auto element = myMap.begin(); element !=myMap.end(); ++element) {
foo(element->first, element->second);
}
您可以使用OpenMP 2.0
#pragma omp parallel
{
size_t cnt = 0;
int ithread = omp_get_thread_num();
int nthreads = omp_get_num_threads();
for(auto element = myMap.begin(); element !=myMap.end(); ++element, cnt++) {
if(cnt%nthreads != ithread) continue;
foo(element->first, element->second);
}
}
每个线程都要经过myMap.size()
迭代器。然而,每个线程只调用foo
myMap.size()/num_threads
。你的方法只运行myMap.size()/num_threads
迭代器。然而,它需要在每次迭代中使用一个临界区。
只要通过nthreads迭代器"快进"的时间远远少于foo
的时间,那么快进方法是有效的,即:
nthreads*time(++elements) << time(foo)
然而,如果foo
的时间顺序是迭代时间,foo
是读写内存,那么foo
可能是内存带宽限制,无论如何都不会随着线程数量的增加而扩展。
你的方法是行不通的——因为一个概念问题和一些bug。
- [bug]你总是会错过第一个元素,因为你做的第一件事是增加元素迭代器。
- [bug]所有线程将遍历整个map,因为元素迭代器不是共享的。顺便说一句,在你的代码中不清楚共享变量"部分"是什么。
- 如果你让元素共享,那么访问它的代码(临界区之外)将看到它当前指向的任何东西,而不管线程是什么。你最终会处理一些元素不止一次,而一些-根本不处理。
使用迭代器并行访问map没有简单的方法,因为map迭代器不是随机访问的。您可能希望手动拆分键,然后在不同的线程上使用键集的不同部分。
- 为什么映射插入和 map.find() 的单次迭代比插入和 map.find() 的两次单独迭代慢得多
- 再次获得 std::map 会更改之前的迭代器
- 如何从map<pair<string,int>,pair<string,Array>>中迭代和查找?
- 使用 map<char,strring> 的迭代器返回指针 map<char,strring>*
- 创建可以遍历 std::map 值的通用模板迭代器的最简单方法是什么?
- 使用 std::for_each 迭代和打印 std::map
- 封装 std::map 以允许迭代,但没有直接密钥访问?
- 从基于迭代器的for循环转换后,如何在map::find()中调用方法
- 为什么我不能在 c++ 中"map"的迭代器中使用"+1"?
- Map中的(字符串的)向量迭代器
- 访问基于范围的循环(如for_each)中的std::map迭代器
- map<int,int>的*迭代器是什么?它不是pair<int,int>
- std::map 擦除 - 将迭代器传递给错误的映射
- C++ Map erase(),迭代器打印 erased 元素
- C 中MAP的反向迭代错过了第一个元素
- 由于从 std::map 派生的类中的 std::map 迭代器导致的内存错误
- C++ std::map 和 std::set 擦除复制值,从而使迭代器失效
- 如果迭代器的迭代器永远不会无效,则是STD :: MAP访问线程安全
- SIGBUS 尝试递增 std::map 迭代器时
- C 迭代器从Typedef std :: Map作为模板参数声明