【发布时间】:2020-09-28 01:51:00
【问题描述】:
我试图通过查看生成的汇编代码来了解使用带有双参数的 std::fma 是否有利,我正在使用标志“-O3”,我正在比较这两个例程的程序集:
#include <cmath>
#define FP_FAST_FMAF
float test_1(const double &a, const double &b, const double &c ){
return a*b + c;
}
float test_2(const double &a, const double &b, const double &c ){
return std::fma(a,b,c);
}
使用Compiler Explorer 工具,这是为两个例程生成的程序集:
test_1(double const&, double const&, double const&):
movsd xmm0, QWORD PTR [rdi] #5.12
mulsd xmm0, QWORD PTR [rsi] #5.14
addsd xmm0, QWORD PTR [rdx] #5.18
cvtsd2ss xmm0, xmm0 #5.18
ret #5.18
test_2(double const&, double const&, double const&):
push rsi #7.65
movsd xmm0, QWORD PTR [rdi] #8.12
movsd xmm1, QWORD PTR [rsi] #8.12
movsd xmm2, QWORD PTR [rdx] #8.12
call fma #8.12
cvtsd2ss xmm0, xmm0 #8.12
pop rcx #8.12
ret
使用 icc 或 gcc 可用的最新版本不会更改程序集。关于这两个例程的性能让我感到困惑的是,虽然对于 test_1 只有一个内存操作( movsd ),但对于 test_2 有三个,并且考虑到内存操作的延迟介于比浮点运算的延迟大一个和两个数量级,test_1 的性能应该更高。因此,建议在哪些情况下使用 std::fma?我的假设有什么错误?
【问题讨论】:
-
这不是一个真正的答案,但如果您删除对
a、b和c的引用,那么test_2的程序集就变成了jmp fma调用,test_1变成 3 条指令。 (compiler explorer 上的示例) -
-O3选项对您的指令集一无所知。我刚刚在两个编译器中添加了-march=native,你的两个函数变得等价(并使用vfmadd213sd指令)。顺便说一句,mulsd和addsd指令包含 移动操作(即从内存中检索数据)。 -
所有三个都必须按照您的代码要求执行内存周期,相同的数字。但是通过使用函数,test_2 可能会变慢。如果优化器可以识别乘法累加并被编程为使用它,那么调用函数总是比让编译器生成它要慢。如果它不能优化,那么它可以去任何一种方式。如果您处理事物的地址而不是事物本身,那么您对性能不感兴趣。所以如何计算是次要的。
-
您的标题具有误导性,暗示您想使用特定指令,但您的实现在很大程度上放弃了通过保存指令可能会看到的性能提升。这个问题应该更像是使用函数与内联生成的代码有什么好处。
-
如果这些是内联处理而不是在函数调用中处理,可能会发生很多差异——避免内存提取、指令重新排序、重叠等。
标签: c++ performance assembly x86-64 fma