FPU,SSE单浮点。哪个更快? sub或mul

时间:2013-09-07 18:36:47

标签: c++ c assembly floating-point

告诉我哪一个更快:submul

我的目标平台是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;
}

感谢所有人;)

1 个答案:

答案 0 :(得分: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[])

此序列的长延迟链是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[]

现在长延迟链是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%。