繁体   English   中英

如何在x86-64上优化C和C ++中的函数返回值?

[英]How to optimize function return values in C and C++ on x86-64?

x86-64 ABI指定两个返回寄存器: raxrdx ,大小均为64位(8字节)。

假设x86-64是唯一的目标平台,这两个功能中的哪一个:

uint64_t f(uint64_t * const secondReturnValue) {
    /* Calculate a and b. */
    *secondReturnValue = b;
    return a;
}

std::pair<uint64_t, uint64_t> g() {
    /* Calculate a and b, same as in f() above. */
    return { a, b };
}

考虑到针对x86-64的C / C ++编译器的当前状态,会产生更好的性能吗? 使用一个版本或其他版本是否存在性能方面的任何陷阱? 编译器(GCC,Clang)总是能够优化在raxrdx返回的std::pair吗?

更新:通常,如果编译器优化了std::pair方法(使用GCC 5.3.0Clang 3.8.0的二进制输出示例),则返回一对更快。 如果没有内联f() ,编译器必须生成代码以将值写入内存,例如:

movq b, (%rdi)
movq a, %rax
retq

但是在g()情况下,编译器就可以:

movq a, %rax
movq b, %rdx
retq

因为将值写入内存的指令通常比将值写入寄存器的指令慢,所以第二个版本应该更快。

由于ABI指定在某些特定情况下必须将两个寄存器用于2字结果,因此任何符合标准的编译器都必须遵守该规则。

但是,对于这些微小的功能,我猜大多数性能都来自内联。

您可能希望使用链接时优化来编译和链接 g++ -flto -O2

我想第二个函数(通过2个寄存器返回一对)可能会稍快一些,并且在某些情况下,GCC编译器可能会内联并优化第一个到第二个。

但如果你关心的话,你真的应该做基准测试。

请注意,ABI指定将任何小结构打包到寄存器中以进行传递/返回(如果它只包含整数类型)。 这意味着返回std::pair<uint32_t, uint32_t>意味着值必须移位+ ORed到rax

这可能仍然比通过内存的往返更好 ,因为为指针设置空间,并将该指针作为额外的arg传递,会产生一些开销。 (除此之外,通过L1缓存的往返非常便宜,比如〜5c延迟。存储/加载几乎肯定会在L1缓存中命中,因为堆栈内存一直在使用。即使它未命中存储转发仍然可能发生,因此执行不会停止,直到ROB填充因为商店无法退出。请参阅Agner Fog的微指南指南以及标签维基上的其他内容。)

无论如何, 这是从gcc 5.3 -O2获得的代码类型 ,使用的函数采用args而不是返回编译时常量值(这将导致movabs rax, 0x... ):

#include <cstdint>
#include <utility>
#define type_t uint32_t

type_t f(type_t * const secondReturnValue, type_t x) {
    *secondReturnValue = x+4;
    return x+2;
}
    lea     eax, [rsi+4]           # LEA is an add-and-shift instruction that uses memory-operand syntax and encoding
    mov     DWORD PTR [rdi], eax
    lea     eax, [rsi+2]
    ret

std::pair<type_t, type_t> g(type_t x) { return {x+2, x+4}; }
    lea     eax, [rdi+4]
    lea     edx, [rdi+2]
    sal     rax, 32
    or      rax, rdx
    ret

type_t use_pair(std::pair<type_t, type_t> pair) {
    return pair.second + pair.first;
}
    mov     rax, rdi
    shr     rax, 32
    add     eax, edi
    ret

所以它真的不错。 调用者和被调用者中有两个或三个insn来打包uint32_t包一对uint32_t值。 尽管如此, uint64_t及返回一对uint64_t值。

如果您专门针对x86-64进行了优化,并且关心具有多个返回值的非内联函数会发生什么,那么即使您分配了这些对,也更喜欢返回std::pair<uint64_t, uint64_t> (或显然是int64_t )。在呼叫者中缩小整数。 请注意,在x32 ABI( -mx32 )中,指针只有32位。 如果你关心那个ABI,那么在优化x86-64时不要假设指针是64位。

如果该对中的任何一个成员是64位,则它们使用单​​独的寄存器 它没有做任何愚蠢的事情,比如在一个reg的高半部分和另一个的低半部分之间分割一个值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM