如何拆分OpenMP同步屏障?
如何拆分OpenMP同步屏障?
先跟你唠唠我的场景,我之前写过一段这样的代码:
std::vector<MyObject> data; do { // 这段循环可能会跑100次甚至更多 prepare(); remaining = compute1(my_data); remaining -= compute2(my_data); cleanup(my_data); } while (remaining > 0);
这里面的prepare()、compute1()、compute2()还有cleanup()每个都是计算密集型的函数,我已经用OpenMP给它们做了并行化,但现在碰到个头疼的问题:每个并行函数内部的全局同步屏障会互相拖后腿,每次循环都要等所有线程完成整个函数才能往下走,整体运行效率被拉低了不少。
针对这个情况,拆分OpenMP同步屏障的核心思路就是把大的并行块拆成更细粒度的局部并行,只在真正需要的节点做同步,而不是让每个函数都触发全局的屏障等待。给你几个具体的实操方法:
别在每个函数内部单独创建并行区域,把整个并行上下文提到循环外面,只初始化一次线程池,这样能避免反复创建销毁线程的开销,也更容易控制同步点:
std::vector<MyObject> data; #pragma omp parallel { do { // 如果prepare不需要并行,用single指定单线程执行,nowait跳过不必要的等待 #pragma omp single nowait { prepare(); } #pragma omp barrier // 只在prepare完成后做一次全局同步 // 把compute1拆成单元素处理的版本,用for+reduction做并行计算 #pragma omp for reduction(-:remaining) for (int i = 0; i < my_data.size(); ++i) { remaining += compute1_single(my_data[i]); } #pragma omp barrier // 等所有线程完成compute1再往下走 // 同理处理compute2 #pragma omp for reduction(+:remaining) for (int i = 0; i < my_data.size(); ++i) { remaining -= compute2_single(my_data[i]); } #pragma omp barrier // compute2完成后同步 #pragma omp single nowait { cleanup(); } } while (remaining > 0); }尽量用局部同步替代全局屏障,如果
compute1和compute2之间没有强数据依赖,完全可以去掉它们之间的屏障,用nowait让两个计算阶段部分重叠,减少等待时间。精准定位同步点:只在必须保证数据一致的节点设置屏障,比如共享数据要被修改或者读取前,其他非必要的同步全部去掉,别让线程做无意义的等待。
总结下来,拆分OpenMP同步屏障的关键就是减少全局同步次数,把同步点精准放在刀刃上,同时复用并行线程池,别让每个小函数都单独触发全局屏障拖慢整体速度。
内容来源于stack exchange
相关产品推荐
相关产品推荐

