如何在无辅助容器下用OpenMP并行遍历std::unordered_map
并行遍历std::unordered_map(无需辅助容器)
好问题!确实,std::unordered_map的迭代器属于前向迭代器,而OpenMP的parallel for默认要求迭代器支持随机访问(能直接计算迭代器间距、执行+=/-=操作),所以直接用i != ht.end()作为终止条件的普通for循环没法适配parallel for。不过不用额外辅助容器,我们有几种靠谱的实现方式:
方法1:用OpenMP taskloop(最适配的原生方案)
你一开始尝试的taskloop其实就是OpenMP专门为非随机访问迭代器设计的指令,它会自动把迭代范围拆分成多个任务,交给线程池并行处理。不过要记得把这段代码放在parallel区域内,否则taskloop不会生效:
std::unordered_map<int, int> ht; // 向map中填充键值对的代码... #pragma omp parallel { #pragma omp taskloop for (auto i = ht.begin(); i != ht.end(); ++i) { // 注意:std::cout线程不安全,并行输出会乱序 // 如果需要有序输出,建议用线程本地缓冲区或加锁 std::cout << i->first << "\t" << i->second << std::endl; } }
这个方案的优势是完全贴合std::unordered_map的迭代器特性,不需要额外计算,性能开销最小。
方法2:用std::distance+std::next模拟随机访问遍历
如果你的场景必须使用parallel for,可以先计算map的元素总数,然后通过索引遍历,用std::next获取对应位置的迭代器:
std::unordered_map<int, int> ht; // 填充map的代码... const size_t map_size = ht.size(); #pragma omp parallel for for (size_t idx = 0; idx < map_size; ++idx) { auto it = std::next(ht.begin(), idx); std::cout << it->first << "\t" << it->second << std::endl; }
注意事项:
std::next对于前向迭代器是逐个递增的,所以遍历的总时间复杂度是O(n)(加上初始size()的O(1),整体还是O(n),但常数比直接遍历大),如果map非常庞大,这个额外开销需要考虑。- 遍历过程中绝对不能修改map的大小(插入/删除元素),否则
map_size会失效,迭代器也可能出错。
方法3:C++17并行算法(现代简洁方案)
如果你使用支持C++17的编译器,可以用标准库的并行std::for_each,只要编译器配置了OpenMP作为并行后端,就能实现无锁的并行遍历:
#include <execution> #include <algorithm> std::unordered_map<int, int> ht; // 填充map的代码... std::for_each(std::execution::par, ht.begin(), ht.end(), [](const auto& key_value_pair) { std::cout << key_value_pair.first << "\t" << key_value_pair.second << std::endl; });
这个方案代码最简洁,不需要手动写OpenMP指令,但需要编译器支持(比如GCC 9+、Clang 10+),编译时要加上-fopenmp等并行编译选项。
内容的提问来源于stack exchange,提问作者letsBeePolite
相关产品推荐
相关产品推荐

