函数参数的数量如何影响性能

问题描述 投票:0回答:1

在编写函数时,我通常会想起

的“干净代码”原则

函数不应有超过 3 个参数。

但是,考虑到下面的这些 x86-64 调用约定,我将其放宽为 4 个参数,因为这通过确保利用 CPU 寄存器而不是比寄存器访问慢的堆栈操作来涵盖跨平台函数。

系统 V AMD64 ABI

Integer/Pointer Arguments 1-6:  RDI, RSI, RDX, RCX, R8, R9
Floating Point Arguments 1-8:   XMM0 - XMM7
Excess Arguments:               Stack

Microsoft x64 调用约定

Integer/Pointer Arguments 1-4:  RCX, RDX, R8, R9
Floating Point Arguments 1-4:   XMM0 - XMM3
Excess Arguments:               Stack

示例

将参数数量限制为 4,确保 Windows 和 Linux 使用寄存器而不是堆栈。

#include <stdio.h>

int Add(int a, int b, int c, int d) { return a + b + c + d; }

int main() {
    int sum = Add(1,2,3,4);
    return 0;
}

Linux 反汇编

mov     ecx, 4
mov     edx, 3
mov     esi, 2
mov     edi, 1
call    Add

Windows 拆解

mov     r9d,4  
mov     r8d,3  
mov     edx,2  
mov     ecx,1  
call    Add

问题

在编写跨平台函数时,这种微优化是一个可行的新咒语吗?

函数不应有超过 4 个参数。

注意

具体用例是在 Windows 和 Linux 上使用 UASM(理解 MASM 语法)汇编代码,因此没有方便的内置优化器。

function assembly x86-64 calling-convention
1个回答
0
投票

您所说的“干净代码”原则很可能就是指:代码整洁和可读性。许多参数通常意味着长行和/或分成多行的参数列表(只需查看具有 10 个参数的 Win32 函数)。

但是,由于您似乎是从严格的性能角度提出这个问题,因此函数有多少个参数并不重要。您不应该减少参数的数量,而应该尝试减少调用的数量。一个好方法是编写编译器可以内联的相当短的函数。

假设我的代码需要添加一批 4 和一批 5,而不是 4 个数字。对于 4 参数函数,我需要这样做:

#include <stdio.h>

int Add(int a, int b, int c, int d) { return a + b + c + d; }

int main() {
    int sum1 = Add(1,2,3,4);
    int sum2 = Add(1,2,3,Add(4,5,0,0));
    return 0;
}

现在假设我使用 5 参数函数:

#include <stdio.h>

int Add(int a, int b, int c, int d, int e) { return a + b + c + d + e; }

int main() {
    int sum1 = Add(1,2,3,4,0);
    int sum2 = Add(1,2,3,4,5);
    return 0;
}

如您所见,第二个版本仅使用 2 次调用(因为我需要 2 个不同的和)。第一个版本使用了 3 次调用。因此,您正在用 2 个

mov
堆栈访问(在 5-arg 版本中)换取整个额外的
call
/
ret
对。请注意,其中还对返回地址执行相同的 2 次堆栈访问(同时还可能以影子堆栈访问、放置在新函数中的金丝雀值等形式引入额外的开销)。

当然,这个例子使用了编译器内联的

Add
函数(常数和也在编译时计算,编译器检测到不需要)。但对于更复杂的实际函数,调用次数越少越好。

© www.soinside.com 2019 - 2024. All rights reserved.