kna*_*123 0 c++ scheduling overhead openmp
我考虑过哪些因素会影响 OpenMP 中的静态调度开销。在我看来,它受到以下因素的影响:
但我是否错过了进一步的因素?也许任务的大小,...?
此外:开销是否与迭代次数线性相关?在这种情况下,我希望使用静态调度和 4 个内核,开销会随着 4*i 次迭代而线性增加。到目前为止正确吗?
编辑:我只对静态(!)调度开销本身感兴趣。我不是在谈论线程启动开销和花费在同步和临界区开销上的时间。
您需要将 OpenMP 创建线程组/线程池的开销与每个线程在 for 循环中操作独立迭代器集的开销分开。
静态调度很容易手动实现(这有时非常有用)。让我们考虑一下我认为最重要的两个静态调度schedule(static),schedule(static,1)然后我们可以将其与schedule(dynamic,chunk).
#pragma omp parallel for schedule(static)
for(int i=0; i<N; i++) foo(i);
Run Code Online (Sandbox Code Playgroud)
相当于(但不一定等于)
#pragma omp parallel
{
int start = omp_get_thread_num()*N/omp_get_num_threads();
int finish = (omp_get_thread_num()+1)*N/omp_get_num_threads();
for(int i=start; i<finish; i++) foo(i);
}
Run Code Online (Sandbox Code Playgroud)
和
#pragma omp parallel for schedule(static,1)
for(int i=0; i<N; i++) foo(i);
Run Code Online (Sandbox Code Playgroud)
相当于
#pragma omp parallel
{
int ithread = omp_get_thread_num();
int nthreads = omp_get_num_threads();
for(int i=ithread; i<N; i+=nthreads) foo(i);
}
Run Code Online (Sandbox Code Playgroud)
由此可以看出,实现静态调度非常简单,因此开销可以忽略不计。
另一方面,如果您想手动实现schedule(dynamic)(与 相同schedule(dynamic,1)),则更复杂:
int cnt = 0;
#pragma omp parallel
for(int i=0;;) {
#pragma omp atomic capture
i = cnt++;
if(i>=N) break;
foo(i);
}
Run Code Online (Sandbox Code Playgroud)
这需要 OpenMP >=3.1。如果您想使用 OpenMP 2.0(对于 MSVC)执行此操作,则需要像这样使用关键
int cnt = 0;
#pragma omp parallel
for(int i=0;;) {
#pragma omp critical
i = cnt++;
if(i>=N) break;
foo(i);
}
Run Code Online (Sandbox Code Playgroud)
这是等效的schedule(dynamic,chunk)(我没有使用原子访问对其进行优化):
int cnt = 0;
int chunk = 5;
#pragma omp parallel
{
int start, finish;
do {
#pragma omp critical
{
start = cnt;
finish = cnt+chunk < N ? cnt+chunk : N;
cnt += chunk;
}
for(int i=start; i<finish; i++) foo(i);
} while(finish<N);
}
Run Code Online (Sandbox Code Playgroud)
显然,使用原子访问会导致更多的开销。这也说明了为什么使用更大的块schedule(dynamic,chunk)可以减少开销。