Ufn*_*d3r 3 c c++ floating-point assembly
告诉我哪一个更快:sub
或者mul
?
我的目标平台是X86; FPU和SSE.
例:
'LerpColorSolution1'使用乘法.
'LerpColorSolution2'使用减法.
哪个更快?
void LerpColorSolution1(const float* a, const float* b, float alpha, float* out)
{
out[0] = a[0] + (b[0] - a[0]) * alpha;
out[1] = a[1] + (b[1] - a[1]) * alpha;
out[2] = a[2] + (b[2] - a[2]) * alpha;
out[3] = a[3] + (b[3] - a[3]) * alpha;
}
void LerpColorSolution2(const float* a, const float* b, float alpha, float* out)
{
float f = 1.0f - alpha;
out[0] = a[0]*f + b[0] * alpha;
out[1] = a[1]*f + b[1] * alpha;
out[2] = a[2]*f + b[2] * alpha;
out[3] = a[3]*f + b[3] * alpha;
}
Run Code Online (Sandbox Code Playgroud)
谢谢大家 ;)
Ste*_*non 10
只是为了好玩:假设你(或你的编译器)矢量化你的两种方法(因为当然你会追逐性能),而你的目标是最近的x86处理器......
将"LerpColorSolution1"直接翻译成AVX指令如下:
VSUBPS dst, a, b // a[] - b[]
VSHUFPS alpha, alpha, alpha, 0 // splat alpha
VMULPS dst, alpha, dst // alpha*(a[] - b[])
VADDPS dst, a, dst // a[] + alpha*(a[] - b[])
Run Code Online (Sandbox Code Playgroud)
该序列的长延迟链是sub-mul-add,在最新的Intel处理器上总延迟为3 + 5 + 3 = 11个周期.吞吐量(假设您只执行这些操作)受端口1利用率的限制,理论峰值为每两个周期一个LERP.(我故意忽略加载/存储流量,并专注于此处执行的数学运算).
如果我们看一下你的"LerpColorSolution2":
VSHUFPS alpha, alpha, alpha, 0 // splat alpha
VSUBPS dst, one, alpha // 1.0f - alpha, assumes "1.0f" kept in reg.
VMULPS tmp, alpha, b // alpha*b[]
VMULPS dst, dst, a // (1-alpha)*a[]
VADDPS dst, dst, tmp // (1-alpha)*a[] + alpha*b[]
Run Code Online (Sandbox Code Playgroud)
现在,长延迟链是shuffle-sub-mul-add,其总延迟为1 + 3 + 5 + 3 = 12个周期; 吞吐量现在受端口0和1的限制,但每两个周期仍然有一个LERP的峰值.您需要为每个LERP操作停用一个额外的μop,这可能会使吞吐量略微降低,具体取决于周围环境.
所以你的第一个解决方案稍好一点; (这并不奇怪 - 即使没有这种分析细节,粗略的指导方针"更少的操作更好"是一个很好的经验法则).
Haswell显着倾向于第一种解决方案; 使用FMA,它在每个端口0,1和5上只需要一个μop,允许每个周期一个LERP的理论吞吐量; 虽然FMA也改进了解决方案2,但它仍然需要4μs,包括需要在端口0或1上执行的3μs.这将解决方案2限制为每1.5个循环一个LERP的理论峰值 - 比解决方案1慢50%.