gpt4 book ai didi

c - 在英特尔 x64 汇编中执行函数的最快(CPU 方式)方式?

转载 作者:太空宇宙 更新时间:2023-11-04 01:02:25 36 4
gpt4 key购买 nike

我一直在阅读有关汇编函数的内容,但对于是使用进入和退出还是仅使用调用/返回指令来快速执行,我感到很困惑。一种方式快而另一种方式更小吗?例如,在不内联函数的情况下,在汇编中执行此操作的最快(stdcall)方法是什么:

static Int32 Add(Int32 a, Int32 b) {
return a + b;
}

int main() {
Int32 i = Add(1, 3);
}

最佳答案

使用call/ret,而不用enter/leave 制作栈帧push&pop rbp/mov rbp, rsp. gcc(使用默认的 -fomit-frame-pointer)只在函数中创建堆栈帧,这些函数在堆栈上进行可变大小分配。 This may make debugging slightly more difficult ,因为 gcc 在使用 -fomit-frame-pointer 编译时通常会发出堆栈展开信息,但您的手写 asm 不会有那个。通常只有在 asm 中编写叶函数才有意义,或者至少是那些不调用许多其他函数的叶函数。

堆栈帧意味着您不必跟踪自函数进入以来堆栈指针发生了多少变化以访问堆栈上的内容(例如函数参数和局部变量的溢出槽)。 Windows 和 Linux/Unix 64 位 ABI 都在寄存器中传递前几个参数,并且通常有足够多的寄存器,您不必将任何变量溢出到堆栈。在大多数情况下,栈帧是指令的浪费。在 32 位代码中,ebp 可用(从 6 到 7 个 GP regs,不计算堆栈指针)比从 14 到 15 有更大的不同。当然,你仍然必须 push/pop rbp 如果你确实使用它,因为在两个 ABI 中它是一个被调用者保存的寄存器,函数不允许破坏。

如果您正在优化 x86-64 asm,您应该阅读 Agner Fog's guides ,并查看 中的一些其他链接。标记维基。

您的函数的最佳实现可能是:

align 16
global Add
Add:
lea eax, [rdi + rsi]
ret
; the high 32 of either reg doesn't affect the low32 of the result
; so we don't need to zero-extend or use a 32bit address-size prefix
; like lea eax, [edi, esi]
; even if we're called with non-zeroed upper32 in rdi/rsi.

align 16
global main
main:
mov edi, 1 ; 1st arg in SysV ABI
mov esi, 3 ; 2nd arg in SysV ABI
call Add
; return value in eax in all ABIs
ret

align 16
OPmain: ; This is what you get if you don't return anything from main to use the result of Add
xor eax, eax
ret

这实际上是what gcc emits对于 Add(),但它仍然会将 main 变成一个空函数,或者如果您 return i 则变成 return 4clang 3.7尊重 -fno-inline-functions 即使结果是编译时常量。它通过尾调用优化和 jmping 到 Add 击败了我的 asm。

请注意,Windows 64 位 ABI 为函数参数使用不同的寄存器。请参阅 x86 标记 wiki 或 Agner Fog 的 ABI 指南中的链接。 Assembler macros可能有助于在 asm 中编写函数,这些函数使用正确的寄存器作为参数,具体取决于您的目标平台。

关于c - 在英特尔 x64 汇编中执行函数的最快(CPU 方式)方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33337354/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com