我正在尝试使用 #pragma omp parallel 在 C++ 中执行一个大循环。 我需要跟踪已经测试过的范围索引值 i (检查点),因为如果作业被中断(终止),则需要恢复作业。 (重做一些工作就可以了。)这段代码在我的基于英特尔的 Linux 计算机上运行良好,但在苹果硅胶上 openmp 时间表到处都是。见下文。如何修复?或者在这种情况下可能有更高级的检查点方法?
#include <iostream>
#include <omp.h>
#include <ostream>
#include <thread>
#include <random>
#include <chrono>
using namespace std;
int main() {
omp_set_num_threads(3);
#pragma omp parallel for schedule(dynamic, 100)
for (int i = 0; i < 20000; ++i){
int id=omp_get_thread_num();
//using puts since it is thread safe
if(!(i % 100)) puts((to_string(i)+": id:"+to_string(id)).c_str());
//here instead of actual job, I am creating a tiny random delay
mt19937_64 eng{random_device{}()}; //seed
uniform_int_distribution<> dist{10, 100};
this_thread::sleep_for(std::chrono::milliseconds{dist(eng)});
}
return 0;
}
Linux 与英特尔:
0: id:2
100: id:1
200: id:0
300: id:1
400: id:0
500: id:2
....
Mac + M3 处理器(带有 mac openmp 库和 clang 编译器):
0: id:0
6700: id:1
13400: id:2
100: id:0
6800: id:1
13500: id:2
200: id:0
6900: id:1
这个时间表对于检查点来说是没有用的。我试图避免静态时间表,因为它效率不高。
Mac 上的编译器+openmp 库似乎使用了动态调度的
nonmonotonic
修饰符:该修饰符相对较新(出现在 OpenMP 4.5 中),尚未在所有 openmp 库中实现(它不在 gcc 13 中,我不知道 gcc 14)。
在 Mac 上,尝试使用以下命令强制旧版单调行为:
schedule(monotonic:dynamic, 100)
实际上,通过
schedule(monotonic:dynamic,chunksize)
调度,每个不活动线程都按顺序归属于下一个未处理的迭代块。使用 schedule(nonmonotonic:dynamic,chunksize)
调度,迭代被预先分配给线程,就像使用 schedule(static)
一样(不管 chunksize
),并且一旦线程完成了其预先分配的工作,它就会被归因于任何未处理的块(也就是说,它是最初归属于其他线程的块)。当工作负载在迭代之间平衡时,steals
计划在最坏的情况下具有与静态 nonmonotonic:dynamic
类似的性能,并且在工作负载不平衡时具有与传统 schedule
计划一样好的性能。另请参阅此答案:https://stackoverflow.com/a/77799465/14778592