草庐IT

c++ - 当 num_threads 变化时,OpenMP 并行区域开销增加

coder 2024-02-24 原文

我试图在程序的不同部分使用不同数量的线程来实现最大加速。但是,发现使用 num_threads 子句切换线程数会产生大量开销。我正在寻找对此的解释,因为根据我的理解,线程池应该始终包含给定数量的线程,而不管调用的实际数量是多少。我也在寻找可能的解决方法。谢谢。

示例代码:

#include<cstdio>
#include<omp.h>

void omp_sum(int ntd) {
    int s = 0;
    #pragma omp parallel num_threads(ntd)
    {
        int i = omp_get_thread_num();
        #pragma omp atomic
        s += i;
    }
}   

int main()
{
    int N = 100;
    int NT1 = 6, NT2 = 12;
    double t;

    t = omp_get_wtime();
    for(int n=0;n<N;n++) {
        omp_sum(NT1);
    }
    printf("%lf\n", (omp_get_wtime() - t) * 1e6 );

    t = omp_get_wtime();
    for(int n=0;n<N;n++) {
        omp_sum(NT2);
    }
    printf("%lf\n", (omp_get_wtime() - t) * 1e6 );

    t = omp_get_wtime();
    for(int n=0;n<N;n++) {
        omp_sum(NT1);
        omp_sum(NT1);
    }
    printf("%lf\n", (omp_get_wtime() - t) * 1e6 );

    t = omp_get_wtime();
    for(int n=0;n<N;n++) {
        omp_sum(NT2);
        omp_sum(NT2);
    }
    printf("%lf\n", (omp_get_wtime() - t) * 1e6 );

    t = omp_get_wtime();
    for(int n=0;n<N;n++) {
        omp_sum(NT1);
        omp_sum(NT2);
    }
    printf("%lf\n", (omp_get_wtime() - t) * 1e6 );
}

示例输出(以我们为单位):

1034.069001
1058.620000
1034.572000
2210.681000
18234.355000

编辑: 运行代码的工作站有 2 个六核 Intel E5-2630L CPU,因此总共应该有 12 个硬件内核和 24 个超线程。我使用的是 Fedora 19 和 GCC 4.8.2。

最佳答案

我可以在我的四核系统/八超线程系统上使用 GCC 4.8 (g++ -O3 -fopenmp foo.cpp) 重现您的结果。我将 N1 更改为 4,将 N2 更改为 8。

你的函数 omp_sum 很简单

pushq   %rbx    
movq    %rdi, %rbx
call    omp_get_thread_num
movq    (%rbx), %rdx
lock addl   %eax, (%rdx)
popq    %rbx
ret

这是循环的汇编代码

for(int n=0;n<N;n++) {
    omp_sum(NT1);
    omp_sum(NT2);
}

.L10
leaq    32(%rsp), %rsi
xorl    %ecx, %ecx
movl    $4, %edx
movl    $_Z7omp_sumi._omp_fn.0, %edi
movl    $0, 28(%rsp)
movq    %rbx, 32(%rsp)
call    GOMP_parallel
leaq    32(%rsp), %rsi
xorl    %ecx, %ecx
movl    $8, %edx
movl    $_Z7omp_sumi._omp_fn.0, %edi
movl    $0, 28(%rsp)
movq    %rbx, 32(%rsp)
call    GOMP_parallel
subl    $1, %ebp
jne .L10

这与循环的程序集几乎相同

for(int n=0;n<N;n++) {
    omp_sum(NT2);
    omp_sum(NT2);
}

唯一的变化是 movl $4, %edx 而不是 movl $8, %edx。所以很难看出是什么导致了问题。所有的魔法都发生在 GOMP_parallel 中。必须查看 GOMP_parallel 的源代码,但我的猜测是 GOMP_parallel 检查最后一次在并行调用中使用的线程数,如果新的并行调用使用不同数量的线程,它会产生一些切换开销。该开销比您的简单函数大得多。

但我不确定为什么这会成为一个问题。在实践中,使用如此短的并行部分是没有意义的(一个人会并行化一个循环,而 N 会更大),因此开销应该不是问题。

编辑:标题为“确定并行区域的线程数”的 OpenMP 3.1 规范的第 2.41 节给出了确定线程数的算法。 The source code for GOMP_parallel from GCC-4.8表明它调用的第一个函数是 gomp_resolve_num_threads

关于c++ - 当 num_threads 变化时,OpenMP 并行区域开销增加,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24440118/

有关c++ - 当 num_threads 变化时,OpenMP 并行区域开销增加的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  3. ruby-on-rails - 启用 Rack::Deflater 时 ETag 发生变化 - 2

    在启用Rack::Deflater来gzip我的响应主体时偶然发现了一些奇怪的东西。也许我遗漏了一些东西,但启用此功能后,响应被压缩,但是资源的ETag在每个请求上都会发生变化。这会强制应用程序每次都响应,而不是发送304。这在没有启用Rack::Deflater的情况下有效,我已经验证页面源没有改变。我正在运行一个使用thin作为Web服务器的Rails应用程序。Gemfile.lockhttps://gist.github.com/2510816有没有什么方法可以让我从Rack中间件获得更多的输出,这样我就可以看到发生了什么?提前致谢。 最佳答案

  4. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  5. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  6. ruby - 带括号和 splat 运算符的并行赋值 - 2

    我明白了:x,(y,z)=1,*[2,3]x#=>1y#=>2z#=>nil我想知道为什么z的值为nil。 最佳答案 x,(y,z)=1,*[2,3]右侧的splat*是内联扩展的,所以它等同于:x,(y,z)=1,2,3左边带括号的列表被视为嵌套赋值,所以它等价于:x=1y,z=23被丢弃,而z被分配给nil。 关于ruby-带括号和splat运算符的并行赋值,我们在StackOverflow上找到一个类似的问题: https://stackoverflow

  7. ruby-on-rails - 缺失区域;使用 :region option or export region name to ENV ['AWS_REGION' ] - 2

    我知道还有其他相同的问题,但他们没有解决我的问题。我不断收到错误:Aws::Errors::MissingRegionErrorinBooksController#create,缺少区域;使用:region选项或将区域名称导出到ENV['AWS_REGION']。但是,这是我的配置开发.rb:config.paperclip_defaults={storage::s3,s3_host_name:"s3-us-west-2.amazonaws.com",s3_credentials:{bucket:ENV['AWS_BUCKET'],access_key_id:ENV['AWS_ACCE

  8. ruby - 使对象的行为类似于 ruby​​ 中并行分配的数组 - 2

    假设您在Ruby中执行此操作:ar=[1,2]x,y=ar然后,x==1和y==2。是否有一种方法可以在我自己的类中定义,从而产生相同的效果?例如rb=AllYourCode.newx,y=rb到目前为止,对于这样的赋值,我所能做的就是使x==rb和y=nil。Python有这样一个特性:>>>classFoo:...def__iter__(self):...returniter([1,2])...>>>x,y=Foo()>>>x1>>>y2 最佳答案 是的。定义#to_ary。这将使您的对象被视为要分配的数组。irb>o=Obje

  9. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  10. += 的 Ruby 方法 - 2

    有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

随机推荐