【问题标题】:Struct in C, are they efficient?C中的结构,它们有效吗?
【发布时间】:2010-03-12 09:44:24
【问题描述】:

我正在阅读一些这样的 C 代码:

double function( int lena,double xa,double ya, double za, double *acoefs, ...,
                 int lenb,double xb,double yb, double zb, double *bcoefs, ...,
                 same for c,
                 same for d )

此函数在代码中被调用超过 100.000 次,因此它对性能至关重要。

我正在尝试扩展此代码,但我想知道将所有参数封装在这样的结构中是否有效(以及这对速度有多大影响)

struct PGTO { int len; double x,y,z ; double *acoefs }

然后访问函数中的参数。

【问题讨论】:

  • 简单的查找方法:配置文件和测试
  • 编译后的代码对于 x86 和 ARM 标准调用约定应该是一样的。
  • ... 是表示可变参数还是省略了某些参数?

标签: c struct performance


【解决方案1】:

Visual c++ 2008 64 位似乎通过让调用者为其堆栈上的结构副本分配空间并将数据项复制到其中然后仅将该副本的地址按值传递给函数来传递结构。

这个简单的例子编译如下-

struct data {
   int a;
   int b;
   int c;
   char d;
   float f;
};




double f2(data d)
{
    return d.a+d.b+d.c+d.d+d.f;
}

编译到这个 -

movsx   eax, BYTE PTR [rcx+12]
add eax, DWORD PTR [rcx+8]
add eax, DWORD PTR [rcx+4]
add eax, DWORD PTR [rcx]
movd    xmm0, eax
cvtdq2ps xmm0, xmm0
addss   xmm0, DWORD PTR [rcx+16]
unpcklps xmm0, xmm0
cvtps2pd xmm0, xmm0
ret 0

因此,基本上,当您传递单个项目时,调用者会将它们推入堆栈,并且函数会相对于堆栈访问它们。当您传递结构时,调用者将数据项复制到堆栈上的一块内存中,然后将其地址传递给函数,该函数相对于该函数访问它们。

第二种方法有更多的汇编语言指令,但据我测试,这两种方法之间的效率没有显着差异,除了几微秒​​。在我的所有测试中,编译器无论如何都希望内联任何调用,除非我强迫它通过指针调用它,因此在你的实际程序中可能根本没有任何东西通过堆栈传递。

在我看来,使用结构更清晰,而且速度似乎没有显着差异,所以选择那个:)

与往常一样,如果对性能有疑问,您需要分析您的确切设置

【讨论】:

    【解决方案2】:

    这个问题的答案很大程度上取决于平台及其调用约定。它还取决于函数是否可以内联以及其他编译器优化。

    我想说,在许多平台上,结构版本可能更有效,因为如果您将指针传递给结构,则需要复制到堆栈的参数更少。

    无论如何,这个问题不容易回答。因此,与所有性能考虑一样:只需测试和配置文件!

    【讨论】:

    • 如果你传递一个指向结构的指针,你传递的是引用,而不是值。
    【解决方案3】:

    首先,在这种情况下,使用结构似乎是正确的方法。代码将更容易阅读和理解,也将变得不那么混乱。

    传递一个指向结构的指针通常比传递大量参数便宜。

    【讨论】:

    • 如果每次调用函数时都需要填充结构体,则不需要。
    • 当然,但是,这又取决于调用代码和源代码的其余部分:) 有很多因素需要考虑。
    【解决方案4】:

    我认为这取决于您填充结构的频率。

    将单个指针传递给结构(4 个字节)将比传递一个 int、3 个双精度和一个指针(32 个字节)需要更少的指令,但是如果您必须在调用之前用这 32 个字节填充结构,那么你就失去了优势。

    无论如何,性能始终是相对的,因此判断它是否值得的唯一方法是查看传递这些参数所花费的时间百分比。为此,我只是让它运行并随机暂停 10 或 20 次。除非我在超过 10% 的时间里通过这些论点抓住了它,否则很可能会有更大的问题需要先解决。

    【讨论】:

      【解决方案5】:

      现代编译器很可能会在这两种情况下生成相同的代码。因此,除非您的编译器不是最先进的,否则您将从结构中获得的唯一(但重要的)好处是提高了可读性。

      【讨论】:

      • 不,不能。调用约定要求在堆栈上传递参数。除非函数声明为static,否则编译器不能改变调用约定。通常情况并非如此。
      • @结构体和单个参数有什么区别?在这两种情况下,参数都将被复制到堆栈中。
      【解决方案6】:

      最好的办法是分析和测试。

      但是,当您将指针传递给 struct 时,较小的参数将被复制到堆栈中。此外,您的函数调用可能看起来更整洁。

      【讨论】:

        【解决方案7】:

        是的,他们是。如果我有这么多,我敢赌一百万美元,性能差异可以忽略不计。

        【讨论】:

          【解决方案8】:

          传递一个结构或参数列表不应该有很大的不同。调用约定既需要按值传递,也需要通过堆栈传递(除非函数可以声明为static)。

          您可以重构代码以启用按引用传递(将指针传递给结构)。但这可能是一次重大的重新设计,它可能会使您的代码更复杂且可读性更低。

          【讨论】:

          • “调用约定”是指所有调用约定吗?你见过的所有调用约定? cdecl 调用约定?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-06-11
          • 2010-12-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-06-15
          • 1970-01-01
          相关资源
          最近更新 更多