将值作为函数参数传递vs计算两次?

时间:2014-02-26 17:40:01

标签: c++ c linux performance optimization

我记得Agner Fog的优秀指南,64位Linux可以通过寄存器传递6个整数函数参数:

http://www.agner.org/optimize/optimizing_cpp.pdf

(第8页)

我有以下功能:

void x(signed int a, uint b, char c, unit d, uint e, signed short f);

我需要传递一个额外的无符号短参数,总共会产生7个参数。但是,我实际上可以从现有的6中获得第7个值。

所以我的问题是以下哪项是更好的表现方法:

  • 在64位Linux上将已计算的值作为第7个参数传递
  • 未传递已经计算的值,但使用现有的6个参数之一再次计算它。

有问题的操作是一个简单的位移:

unsigned short g = c & 1;

不完全理解x86汇编程序我不太清楚寄存器是多么珍贵以及将值重新计算为局部变量是否更好,而不是通过函数调用作为参数传递?

我的信念是,计算两次值会更好,因为它是一个简单的1 CPU循环任务。

编辑我知道我可以对此进行分析 - 但我也想了解两种方法下的情况。有第7个参数这是否意味着涉及缓存/内存,而不是寄存器?

2 个答案:

答案 0 :(得分:4)

传递参数的机器约定称为application binary interface(或ABI),而对于Linux x86-64则在x86-64 ABI spec中描述。另请参阅x86 calling conventions wikipage。

在您的情况下,将c & 1作为附加参数传递可能是不值得的(因为在堆栈上传递了7 th 参数)。

不要忘记当前处理器核心(在台式机或笔记本电脑上)经常处于out-of-order execution并且superscalar,因此c & 1操作可以与其他操作并行完成并且可能花费“没有”。

但是将这些微优化留给编译器。如果您非常关注性能,请使用最近的GCC 4.8编译器和gcc-4.8 -O3 -flto编译和链接(即启用link-time optimization)。

BTW,缓存性能比这种微优化更具相关性。单个高速缓存未命中可能花费与数百个CPU机器指令相同的时间(例如250纳秒)。传闻目前的CPU大多等待缓存。您可能希望向__builtin_prefetch添加一些明确(和明智)的调用(请参阅this questionthis answer)。但是添加太多这些预取会降低代码速度。

最后,代码的可读性和可维护性应该比原始性能更重要!

答案 1 :(得分:2)

Basile的回答很好,我只想指出另一件事要记住:
a)堆栈很可能在L1缓存中,因此在堆栈上传递参数不应超过约3个周期。
b)ABI(在这种情况下为x86-64 System V)需要恢复被破坏的寄存器。一些由呼叫者保存,另一些由被呼叫者保存。显然,如果再次需要原始内容,则调用者必须保存用于传递参数的寄存器。但是当您的函数使用的寄存器多于调用者保存的寄存器时,函数需要计算的任何其他临时结果必须进入被调用者保存的寄存器。因此,该函数最终将堆栈中的寄存器溢出,重新使用寄存器作为临时变量,然后将原始值弹回。
避免访问内存的唯一方法是使用更小,更简单的函数,这需要更少的临时变量。