c++ - 对 OpenMP 中静态调度开销的影响

标签 c++ openmp scheduling overhead

我考虑了哪些因素会影响 OpenMP 中的静态调度开销。 在我看来,它受到以下因素的影响:

  • CPU 性能
  • 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);

等同于(但不一定等于)

#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);
}

#pragma omp parallel for schedule(static,1)
for(int i=0; i<N; i++) foo(i);

相当于

#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);
}

由此您可以看出,实现静态调度非常简单,因此开销可以忽略不计。

另一方面,如果您想手动实现 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);                                    
}

这需要 OpenMP >=3.1。如果您想使用 OpenMP 2.0(对于 MSVC)执行此操作,则需要像这样使用 critical

int cnt = 0;
#pragma omp parallel
for(int i=0;;) {
    #pragma omp critical   
    i = cnt++;
    if(i>=N) break;
    foo(i);
} 

这是等同于 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);
}

显然使用原子访问会导致更多的开销。这也说明了为什么对 schedule(dynamic,chunk) 使用更大的 block 可以减少开销。

关于c++ - 对 OpenMP 中静态调度开销的影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30577570/

相关文章:

c++ - 使用BFS算法寻找最短路径

c++ - 如果 (a) b = c, d = e, return; 为什么这将无法编译?

c++ - std::random_device 的线程安全

c++ - 节点预分配 vector 中的无锁树节点分配

algorithm - 遇到具有等效端点的间隔时的间隔调度最大化

c++ - 如何在 Arduino/C++ 中计算校验和?

c++ - 为什么 bash 在进程死后不打印 "Broken pipe"日志?

c++ - 选择并行化方案要考虑什么?

linux - linux内核的 'expired'队列中是否存在实时进程?

algorithm - 是否有针对 "maker' 的调度进行优化的调度算法”?