[这个问题和它的回答已经比较全面地讨论了传递结构体与结构体指针的优缺点。本回答旨在讨论本问题中提到的特定情况,即8字节结构体与8字节指针以及ABI在寄存器中传递参数的情况。]
在运行Linux的64位Intel CPU上,ABI要求通过寄存器传递8字节参数,直到没有参数为止。例如,第一个参数通过%rdi
寄存器传递。这并不是优化的问题,而是ABI的要求。
在这种特殊情况下(8字节结构体与8字节指针),指针和结构体都将通过一个单一的寄存器传递。也就是说,两种情况都不使用栈。实际上,如果您有一个足够简单的函数,例如:
int
add (struct Point p)
{
return p.x + p.y;
}
如果使用gcc -O1
编译,函数甚至不会有一个栈帧。
在生成的代码中可以看到这一点(x86_64 Linux gcc
5.1,使用-O1
):
# Passing the struct:
movq %rdi, %rax
sarq $32, %rax
addl %edi, %eax
ret
# Passing a pointer to the struct:
# [each (%rdi) is a memory access]
movl 4(%rdi), %eax
addl (%rdi), %eax
ret
但是,如您所见,指针版本会访问内存两次。因此,传递值更快。传递指针将生成内存访问以获取结构体的成员。还有一种额外的风险,即结构体可能在未被CPU缓存的内存块上,并且访问将导致缓存未命中。这不应该发生,因为通常,调用者只需访问相同的结构体,因此它在缓存中。
在32位Linux上,int
仍然是4个字节,但指针变小了(从8减少到4)。由于参数在堆栈上传递,这意味着传递指针可以在堆栈上节省4个字节(与8个字节的结构体相比,vs 4个字节的指针)。但我仍然喜欢按值传递,因为它可以改善空间局部性。
%rdi
寄存器传递。在这种特定情况下,指针和结构体都将通过一个单一寄存器传递。因此,传递值更快,因为传递指针将生成2个不必要的内存访问。 - ArjunShankar