【问题标题】:Do C++ compilers optimize pass by const reference POD parameters into pass by copy?C++ 编译器是否将按 const 引用 POD 参数传递优化为按副本传递?
【发布时间】:2011-01-03 21:04:35
【问题描述】:

考虑以下几点:

struct Point {double x; double y;};

double complexComputation(const& Point p1, const Point& p2)
{
    // p1 and p2 used frequently in computations
}

编译器是否将传递引用优化为传递复制以防止频繁取消引用?换句话说,将complexComputation 转换为:

double complexComputation(const& Point p1, const Point& p2)
{
    double x1 = p1.x; double x2 = p2.x;
    double y1 = p1.y; double y2 = p2.y;
    // x1, x2, y1, y2 stored in registers and used frequently in computations
}

既然Point是一个POD,那么背着调用者做一个副本应该不会有什么副作用吧?

如果是这种情况,那么我总是可以通过 const 引用传递 POD 对象,无论多小,而不必担心最佳传递语义。对吧?

编辑: 我对 GCC 编译器特别感兴趣。我想我可能需要编写一些测试代码并查看 ASM。

【问题讨论】:

  • 我试着搜索这个问题,但我一直在想出关于按值传递、按引用传递等的 abc 的命中率。如果已经讨论过了,我很抱歉。
  • 通常,相反的方法可能是更好的方法 (cpp-next.com/archive/2009/08/want-speed-pass-by-value)。按值传递,让编译器在需要的时候转成按引用传递

标签: c++ optimization


【解决方案1】:

我不能代表每个编译器,但一般的答案是否定的。它不会进行优化。

请参阅GOTW#81,了解如何在 C++ 中强制转换为 const 不会像某些人认为的那样影响优化。

【讨论】:

    【解决方案2】:

    如果需要,您的编译器可以绝对将 Point 成员变量提升到寄存器中。但是,这与编译器将函数调用本身转换为按值传递不同。

    您应该检查生成的程序集以了解正在执行哪些优化。

    而 FWIW,我使用的一般规则是尽可能按值传递所有原始类型,并通过 const 引用传递所有类/UDT(或非 POD),并让编译器找出最好的方法。我们不必担心编译器在做什么的细节,它比我们聪明得多。

    【讨论】:

    • 我同意不要担心,除非基准测试/分析告诉我们这样做。但我只是好奇编译器是否确实可以进行这种类型的优化。
    【解决方案3】:

    有 2 个问题。

    首先,编译器不会将 pass-by-ref 转换为 pass-by-value,特别是如果 complexComputation 不是 static(即可以被外部对象使用)。

    原因是 API 兼容性。对于 CPU,没有“参考”之类的东西。编译器会将引用转换为指针。参数通过堆栈或寄存器传递,因此调用complexComputation 的代码可能会被调用为(假设double 的长度暂时为4):

    str x1, [r7, #0x20]
    str y1, [r7, #0x24]
    str x2, [r7, #0x50]
    str y2, [r7, #0x54]
    push r7, #0x20     ; push address of p1 onto the stack
    push r7, #0x50     ; push address of p2 onto the stack
    call complexComputation
    

    只有 8 个字节被压​​入堆栈。

    另一方面,通过副本传递会将整个结构体压入堆栈,因此汇编代码看起来像

    push x1    ; push a copy of p1.x onto the stack
    push y1    ; push a copy of p1.y onto the stack
    push x2    ; push a copy of p2.x onto the stack
    push y2    ; push a copy of p2.y onto the stack
    call complexComputation
    

    请注意,这一次将 16 个字节压入堆栈,内容是数字,而不是指针。如果complexComputation 改变了它的参数传递语义,输入就会变成垃圾,你的程序可能会崩溃。


    另一方面,优化

    double complexComputation(const Point& p1, const Point& p2) {
        double x1 = p1.x; double x2 = p2.x;
        double y1 = p1.y; double y2 = p2.y;
        // x1, x2, y1, y2 stored in registers and used frequently in computations
    }
    

    很容易做到,因为编译器可以识别出哪些变量经常被使用并且 将它们存储到保留寄存器中(例如 ARM 架构中的 r4 ~ r13,以及许多 sXX/dXX 寄存器),以便更快地访问。


    毕竟,如果你想知道编译器是否做了什么,你总是可以反汇编得到的对象并进行比较。

    【讨论】:

      猜你喜欢
      • 2017-11-11
      • 2021-09-09
      • 2010-09-21
      • 1970-01-01
      • 2022-01-19
      • 2012-11-04
      • 1970-01-01
      • 2012-07-29
      • 2017-12-14
      相关资源
      最近更新 更多