C中的结构,它们有效吗?

C中的结构,它们有效吗?,c,struct,performance,C,Struct,Performance,我正在读一些类似这样的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 ) 此函数在代码中的调用次数超过10

我正在读一些类似这样的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 }

然后访问函数中的参数。

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

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


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

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

最好的方法是分析和测试


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

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


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

是的,它们是。如果我有那么多的话,我会打赌一百万美元,性能差异可以忽略不计。

传递一个结构或参数列表不会产生很大的差异。调用约定要求通过值传递和通过堆栈传递(除非函数可以声明为
静态


您可以重构代码以启用按引用传递(将指针传递到结构)。但这可能是一个重大的重新设计,它可能会使您的代码更复杂,可读性更低
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
因此,基本上,当您传递单个项时,调用方将它们推送到堆栈上,并且函数相对于堆栈访问它们。当您传递一个structure时,调用者会将数据项复制到堆栈上的一块内存中,然后将该数据项的地址传递到函数中,该函数会相对于该数据项访问这些数据项

第二种方法有两个以上的汇编语言指令,但它是正确的,所以从我的测试中可以看出,除了几微秒之外,这两种方法的效率没有显著差异。在我所有的测试中,编译器都希望内联任何调用,除非我强制它通过指针调用它,所以在实际程序中,可能根本没有任何东西通过堆栈传递

在我看来,使用struct更为清晰,而且速度上似乎没有显著差异,所以请使用该结构:)


与往常一样,如果对性能有疑问,您需要分析您的确切设置,我认为这取决于您填充结构的频率

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


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

简单的方法:profile和test编译的代码对于x86和ARM标准调用约定应该是相同的。
是表示varargs还是省略了一些参数?不,不能。调用约定要求在堆栈上传递参数。除非函数声明为
static
,否则编译器无法改变调用约定。通常情况并非如此。@结构和单个参数之间有什么区别?在这两种情况下,参数都会被复制到堆栈上。如果传递一个指向结构的指针,则是通过引用,而不是通过值。如果每次调用函数时都需要填充结构。当然,但是,再一次,这取决于调用代码和源代码的其余部分:)有很多因素需要考虑。“呼叫约定”是指所有呼叫约定?您见过的所有呼叫约定?cdecl呼叫约定?