【问题标题】:Influence on the static scheduling overhead in OpenMP对 OpenMP 中静态调度开销的影响
【发布时间】:2015-08-15 03:40:15
【问题描述】:

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

  • CPU 性能
  • OpenMP 运行时库的具体实现
  • 线程数

但我是否遗漏了其他因素?也许是任务的大小,...?

此外:开销是否线性依赖于迭代次数? 在这种情况下,我希望具有静态调度和 4 个内核,开销随着 4*i 迭代线性增加。到目前为止正确吗?

编辑: 我只对静态(!)调度开销本身感兴趣。我不是在谈论线程启动开销和同步所花费的时间以及临界区开销。

【问题讨论】:

    标签: c++ openmp scheduling overhead


    【解决方案1】:

    您需要将 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)执行此操作,则需要像这样使用关键

    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) 使用更大的块可以减少开销。

    【讨论】:

    • 感谢您的详细回答。但是,我已经知道您的解释。我只对调度开销本身感兴趣,这意味着没有线程启动开销,也没有同步(例如屏障)和临界区开销。据我了解,调度开销是将任务分配给线程所花费的时间(可能包括决定将哪个任务分配给哪个线程所花费的时间。所以我很感兴趣,哪些因素会影响此调度开销。也许我没有制定我的问题够清楚
    • @knacker123,我描述了调度开销。我没有描述启动开销。你看过我的回答吗?对于静态调度,开销是计算开始和结束的成本,这是微不足道的。它具有恒定时间(O(1)),并且不依赖于线程数或迭代次数。但是,使用动态调度,开销确实取决于迭代次数。我的回答不是很清楚吗?
    • 您的动态调度示例可能有点误导,因为您可以使用原子更新共享数据。
    • @jch,感谢您的评论。我会看看我是否可以修复它并在有时间时更新我的​​答案。
    • @jch,我不知道如何使用atomic 执行此操作。你会怎么做?
    猜你喜欢
    • 2021-03-18
    • 2011-05-14
    • 2021-03-09
    • 2011-11-10
    • 2019-11-11
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    相关资源
    最近更新 更多