【问题标题】:Passing struct pointer vs passing struct传递结构指针与传递结构
【发布时间】:2015-12-11 12:21:33
【问题描述】:

我最近编写了很多在函数周围传递structs 的程序,以避免全局变量。但是,我想知道传递 struct 本身或其指针是否更有效。听起来应该是这样,因为指针(在我的 64 位 GNU/Linux 系统上)是 8 个字节,而充满指针的 struct 显然远不止于此。

但是,如果我有这个struct:

struct Point {
    int x;
    int y;
}

这是8个字节,与指针大小相同,将整个struct传递给函数,还是传递指针更好?我对C内存分配相当精通,所以在初始化指针的时候使用malloc和朋友们是没有问题的。

我的另一个想法是,如果结构很大,直接传递结构可能会占用大量堆栈空间。但是简单地使用指针会耗尽内存,这可以很容易地freeed。

【问题讨论】:

  • 通过引用传递通常更有效,具体取决于结构的大小。但是对于像您的 struct Point 这样的小型结构,它并没有太大的区别(如果有的话)。
  • 您还应该考虑可能的编译器优化。如果您传递对结构的引用,编译器可以使用其中一个处理器寄存器来保存它并避免内存访问速度较慢。在简单的应用程序中您不会看到太大的差异,但在复杂的场景中它可以为您提供更好的性能。
  • 我认为“优化”论点并不成立。在运行 Linux 的 64 位 Intel 上,ABI 要求前 8 字节参数通过%rdi 寄存器传递。在这种特殊情况下,指针和结构都将通过该单个寄存器传递。因此,传递值更快,因为传递指针会产生 2 次不必要的内存访问。
  • @Ari0nhh 即在这种特定情况下(8 字节结构与 8 字节指针在运行 Linux 的 64 位英特尔 CPU 上指向 8 字节结构),传递指针实际上是 较慢
  • 再想一想:在 32 位上,调用者也必须将这两个值放入堆栈中。这将比仅放置指针多一次内存访问。所以两者都会导致相同数量的内存访问,但传递值将在堆栈上占用 4 个额外字节。所以考虑到这一点,我猜在 32 位上,传递指针获胜。但不要忘记缓存未命中。

标签: c linux pointers struct


【解决方案1】:

[This question 它的答案对传递结构与结构指针的优缺点进行了相当彻底的一般处理。此答案旨在处理此问题中提到的特定情况,即 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 上,ints 继续为 4 字节,但指针变小(8 降至 4)。而且由于参数是在堆栈上传递的,这意味着传递 指针在堆栈上保存 4 个字节(8 字节结构,与 4 字节指针)。 但由于空间局部性得到改善,我仍然喜欢按值传递。

【讨论】:

    猜你喜欢
    • 2015-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    相关资源
    最近更新 更多