【问题标题】:Passing value as a function argument vs calculating it twice?将值作为函数参数传递与计算两次?
【发布时间】:2014-03-29 17:36:54
【问题描述】:

我记得在 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 个参数是否意味着涉及缓存/内存,而不是寄存器?

【问题讨论】:

  • 我会说这几乎是不可能预测的。您的函数可能是内联的,在这种情况下这并不重要。此外,乱序执行可以隐藏大量内存访问的延迟,使其确实很难预测。
  • 我实际上可以从现有的 6 个中推导出第 7 个的值。”我认为这是一个设计问题。
  • 不管怎样,在函数中重新计算变量而不是传递变量以将内容保存在寄存器中的花哨名称是rematerialization
  • @alk 设计问题是什么?如果您的函数需要所有七个变量并且第七个变量的计算成本很高,那么即使它们在逻辑上不是独立的,传递所有七个变量似乎也是合理的。

标签: c++ c linux performance optimization


【解决方案1】:

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

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

不要忘记当前的处理器内核(在台式机或笔记本电脑上)通常在执行 out-of-order executionsuperscalar,因此 c & 1 操作可以与其他操作并行完成,并且可能会花费“零” .

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

顺便说一句,缓存性能比这种微优化更重要。单个缓存未命中可能需要与数百个 CPU 机器指令相同的时间(例如 250 纳秒)。有传言说,当前的 CPU 主要是在等待缓存。您可能希望向__builtin_prefetch 添加一些显式(且明智的)调用(请参阅this questionthis answer)。但是添加太多这些预取会减慢您的代码速度。

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

【讨论】:

  • 另请注意,这很可能在例如64 位 SPARC 或 Power 或其他 - 运行 Linux 的每种类型的系统都可以有显着不同的 ABI。这意味着这个级别的优化通常最好留给编译器,除非你想为不同的平台维护多个版本的代码(或者如果你只关心一个平台,但从长远来看,这通常被证明是短视的。 ..).
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 2017-01-13
    • 2018-01-11
    • 2012-11-13
    • 1970-01-01
    • 1970-01-01
    • 2012-11-17
    • 2021-04-13
    • 2023-04-02
    相关资源
    最近更新 更多