草庐IT

c++ - 按引用和按值传递时的 gcc 程序集

coder 2024-02-23 原文

我有一个简单的函数来计算 两个双数组:

#include <stdlib.h>
#include <emmintrin.h>

struct S {
    double *x;
    double *y;
    double *z;
};

void f(S& s, size_t n) {
    for (int i = 0; i < n; i += 2) {
        __m128d xs = _mm_load_pd(&s.x[i]);
        __m128d ys = _mm_load_pd(&s.y[i]);
        _mm_store_pd(&s.z[i], _mm_mul_pd(xs, ys) );
    }
    return;
}

int main(void) {
    S s;
    size_t size = 4;
    posix_memalign((void **)&s.x, 16, sizeof(double) * size);
    posix_memalign((void **)&s.y, 16, sizeof(double) * size);
    posix_memalign((void **)&s.z, 16, sizeof(double) * size);
    f(s, size);
    return 0;
}

请注意,函数 f 的第一个参数是通过引用传入的。 让我们看看 f() 的结果汇编(我删除了一些不相关的 件,插入评论并放置一些标签):

$ g++ -O3 -S asmtest.cpp 


        .globl      _Z1fR1Sm
_Z1fR1Sm:
        xorl        %eax, %eax
        testq       %rsi, %rsi
        je  .L1
.L5:
        movq        (%rdi), %r8             # array x   (1)
        movq        8(%rdi), %rcx           # array y   (2)
        movq        16(%rdi), %rdx          # array z   (3)
        movapd      (%r8,%rax,8), %xmm0     # load x[0]
        mulpd       (%rcx,%rax,8), %xmm0    # multiply x[0]*y[0]
        movaps      %xmm0, (%rdx,%rax,8)    # store to y
        addq        $2, %rax                # and loop
        cmpq        %rax, %rsi
        ja  .L5

注意数组 x, y, z 的地址被加载到通用 在每次迭代时注册,请参见语句 (1)、(2)、(3)。为什么gcc不动 这些指令在循环外?

现在制作结构的本地拷贝(不是深层拷贝):

void __attribute__((noinline)) f(S& args, size_t n) {
    S s = args;
    for (int i = 0; i < n; i += 2) {
        __m128d xs = _mm_load_pd(&s.x[i]);
        __m128d ys = _mm_load_pd(&s.y[i]);
        _mm_store_pd(&s.z[i], _mm_mul_pd(xs, ys) );
    }
    return;
}

程序集:

_Z1fR1Sm:
.LFB525:
        .cfi_startproc
        xorl        %eax, %eax
        testq       %rsi, %rsi
        movq        (%rdi), %r8     # (1)
        movq        8(%rdi), %rcx   # (2)
        movq        16(%rdi), %rdx  # (3)
        je  .L1
.L5:
        movapd      (%r8,%rax,8), %xmm0
        mulpd       (%rcx,%rax,8), %xmm0
        movaps      %xmm0, (%rdx,%rax,8)
        addq        $2, %rax
        cmpq        %rax, %rsi
        ja  .L5
.L1:
        rep ret

请注意,与前面的代码不同, 加载 (1)、(2)、(3) 现在在循环之外。

我希望能解释一下为什么这两个组件 代码不同。内存别名在这里相关吗? 谢谢。

$ gcc --版本 gcc (Debian 5.2.1-21) 5.2.1 20151003

最佳答案

是的,gcc 在循环的每次迭代中重新加载 s.xs.y 因为 gcc 不知道 &s.z[i]对于通过引用传递给 f(S&, size_t)S 对象的某些 i 别名部分。

使用 gcc 5.2.0,将 __restrict__ 应用于 S::z 并将 s 引用参数应用于 f(),即:

struct S {
    double *x;
    double *y;
    double *__restrict__ z;
};

void f(S&__restrict__ s, size_t n) {
    for (int i = 0; i < n; i += 2) {
        __m128d xs = _mm_load_pd(&s.x[i]);
        __m128d ys = _mm_load_pd(&s.y[i]);
        _mm_store_pd(&s.z[i], _mm_mul_pd(xs, ys));
    }
    return;
}

.. 导致 gcc 生成:

__Z1fR1Sm:
LFB518:
    testq   %rsi, %rsi
    je  L1
    movq    (%rdi), %r8
    xorl    %eax, %eax
    movq    8(%rdi), %rcx
    movq    16(%rdi), %rdx
    .align 4,0x90
L4:
    movapd  (%r8,%rax,8), %xmm0
    mulpd   (%rcx,%rax,8), %xmm0
    movaps  %xmm0, (%rdx,%rax,8)
    addq    $2, %rax
    cmpq    %rax, %rsi
    ja  L4
L1:
    ret

使用 Apple Clang 700.1.76,只需要 s 引用上的 __restrict__:

__Z1fR1Sm:                              ## @_Z1fR1Sm
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    testq   %rsi, %rsi
    je  LBB0_3
## BB#1:                                ## %.lr.ph
    movq    (%rdi), %rax
    movq    8(%rdi), %rcx
    movq    16(%rdi), %rdx
    xorl    %edi, %edi
    .align  4, 0x90
LBB0_2:                                 ## =>This Inner Loop Header: Depth=1
    movapd  (%rax,%rdi,8), %xmm0
    mulpd   (%rcx,%rdi,8), %xmm0
    movapd  %xmm0, (%rdx,%rdi,8)
    addq    $2, %rdi
    cmpq    %rsi, %rdi
    jb  LBB0_2
LBB0_3:                                 ## %._crit_edge
    popq    %rbp
    retq
    .cfi_endproc

关于c++ - 按引用和按值传递时的 gcc 程序集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33188316/

有关c++ - 按引用和按值传递时的 gcc 程序集的更多相关文章

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

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

  2. ruby-on-rails - 无法在centos上安装therubyracer(V8和GCC出错) - 2

    我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e

  3. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  4. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  5. ruby-on-rails - 如何生成传递一些自定义参数的 `link_to` URL? - 2

    我正在使用RubyonRails3.0.9,我想生成一个传递一些自定义参数的link_toURL。也就是说,有一个articles_path(www.my_web_site_name.com/articles)我想生成如下内容:link_to'Samplelinktitle',...#HereIshouldimplementthecode#=>'http://www.my_web_site_name.com/articles?param1=value1¶m2=value2&...我如何编写link_to语句“alàRubyonRailsWay”以实现该目的?如果我想通过传递一些

  6. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  7. 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.你能做的最好的事情是:

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

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

  9. ruby - 如何将 Puma::Configuration 传递给 Sinatra? - 2

    这是我的网络应用:classFront我是这样开始的(请不要建议使用Rack):Front.start!这是我的Puma配置对象,我不知道如何传递给它:require'puma/configuration'Puma::Configuration.new({log_requests:true,debug:true})说真的,怎么样? 最佳答案 配置与您运行的方式紧密相关puma服务器。运行的标准方式puma-pumaCLI命令。为了配置puma配置文件config/puma.rb或config/puma/.rb应该提供(参见examp

  10. jquery - 如何将 AJAX 变量从 jQuery 传递到他们的 Controller ? - 2

    我有一个电子邮件表格。但是我正在制作一个测试电子邮件表单,用户可以在其中添加一个唯一的电子邮件,并让电子邮件测试将其发送到该特定电子邮件。为了简单起见,我决定让测试电子邮件通过ajax执行,并将整个内容粘贴到另一个电子邮件表单中。我不知道如何将变量从我的HAML发送到我的Controllernew.html.haml-form_tagadmin_email_blast_pathdoSubject%br=text_field_tag'subject',:class=>"mass_email_subject"%brBody%br=text_area_tag'message','',:nam

随机推荐