【问题标题】:How large structs can be passed by value efficiently?可以有效地按值传递多大的结构?
【发布时间】:2009-05-13 14:47:34
【问题描述】:

经验法则是通过值传递小的结构是可以的,而较大的应该做指针。

我的问题是这个分界点到底在哪里?在你最好通过指针传递它们之前,结构可以有多大。

我知道这会因平台而异,但我认为可以给出一些粗略的估计。一两年前,我试图在 PPC 架构上弄清楚这一点,令我惊讶的是,一个人可以通过价值有效地传递大量数据。由于 PPC 中有大量寄存器,因此认为 10 个双精度值左右就可以了。通过指针实际上涉及更多的内存复制。

但是我现在在英特尔上,我希望事情可能会有所不同。由于传统上 CPU 没有那么多寄存器,但在 64 位或浮点寄存器上可能会有所不同?

【问题讨论】:

  • 这取决于....你需要基准测试...
  • Mitch 是正确的,唯一知道的方法是进行基准测试。您的答案会因您测试的平台而异。
  • 我想我的问题是我不知道如何正确地对它进行基准测试。我担心一个简单的例子太容易被编译器优化,而不能反映实际使用情况。进行过早的优化可能看起来很愚蠢,但这是我一直在努力争取良好性能的事情,所以我不想做出不必要的愚蠢选择。它会影响我的整个 API 设计,所以我不想在以后更改所有这些。

标签: c performance optimization struct pointers


【解决方案1】:

好的,所以我尝试遵循建议并使用指针和值来分析我的代码。我还查看了汇编代码。 x86 上的性能特征似乎与 PPC 有很大不同。在 PPC 上,C 的二进制接口指定将参数放入寄存器(有很多可供选择),但似乎即使在 64 位 x86 上也需要将参数放入堆栈。

这就解释了为什么在 x86 上通过指针传递似乎总是更快。但是我注意到编译器非常渴望内联。因此,我以哪种方式做到这一点并不重要。所以我想结论是使用任何你方便的传递。

我认为优惠是通过价值传递的,因为处理价值的副本更安全一些。我的测试用例是一个由 4 个双精度组成的结构(所以我猜在大多数平台上它是 32 个字节)。

【讨论】:

    【解决方案2】:

    如果您在网上搜索,您会发现一些关于通过引用和值传递的字节大小的指南。我几乎不相信它。 知道特定结构存在问题的唯一方法是

    简介

    这是 100% 知道存在问题的唯一方法。

    在反对者加入之前。是的,那里有一些明显的案例。例如,如果它有 100 个成员,我不会按值传递一个结构。但这不是性能问题,而是堆栈空间问题。

    【讨论】:

    • 投反对票。这不是答案 - 不要告诉人们搜索网络。发布事实并参考它们。
    【解决方案3】:

    在 C++ 中,有一个规则是传递 everything 不在以下列表中作为 const 引用,因为性能基本上不会变差。例外列表是:

    • 基本类型(int 等),
    • 指针,
    • 空类型(标签类型),
    • 类函数类型(函子),以及
    • 迭代器。

    我不确定这是否可以直接应用于 C(除了 C 没有的明显类型),但可能适用类似的准则。

    【讨论】:

    • 我在编写 C++ 时遵循这种模式,但严格来说,我不确定这是否总是最优的。据我所知,const MyClass &var 是作为 const 指针实现的。对于小对象(例如 2-3 个双精度对象),通过指针传递比通过值传递要慢。
    • 访问指针当然会产生开销,但处理器制造商意识到指针的必要性。因此,他们采取了预防措施。例如,AMD 优化指南明确建议在此处使用指针,因为处理器的开发考虑了这种用法。长话短说:即使对于小型结构,指针也可能比按值传递更快。内联当然可以进一步修改这种行为。
    • 您发布的例外列表是否有任何“官方”来源?我打算写一个问题天气通过引用传递基本类型是有意义的。
    • 我发布的规则或多或少 1:1 取自 Meyers 的 Effective C++。这可能不是官方的,但它并没有更接近源头。但标准库的使用也反映了这一点(例如,严格遵守此规则的 algorithms 标头)。
    • 使用 const 引用是否会阻止您使用 __attribute__((const)) 标记函数,因为参数/返回值必须通过全局内存?如果您的函数经常使用相同的 args 调用,那么将大堆栈参数传递给 __attribute__((const)) 函数会更好吗?
    【解决方案4】:

    某些编译器可能会为您确定最佳大小。如果我没记错的话,如果结构超过一定大小,TI28xx 编译器会自动将按值传递转换为按引用传递。

    【讨论】:

      【解决方案5】:

      不要忽视对齐在测试中的作用。如果您传递浮点数或双精度数并且您的结构未在适当的边界上对齐,则处理器可能最终会获取您的部分值,对其进行移位,然后在存储之前将其余部分进行 OR 运算。我认为大多数现代编译器都会 DTRT(通过在声明结构时对齐结构),但如果您正在优化空间,那么这可能是一个问题。

      嗯,现在我想起来了,对此持保留态度,因为自从 Pentium Pro 以来,我还没有在 x86 架构上进行任何低级编码...

      【讨论】:

        【解决方案6】:

        我通常通过值传递原始类型,通过引用传递其他所有类型。 这是我的经验法则。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-04
          • 2014-04-13
          • 1970-01-01
          • 2015-01-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多