【问题标题】:SSE slower than FPU?SSE 比 FPU 慢?
【发布时间】:2012-02-09 11:18:16
【问题描述】:

我有一大段代码,它的部分主体包含这段代码:

result = (nx * m_Lx + ny * m_Ly + m_Lz) / sqrt(nx * nx + ny * ny + 1);

我已将其矢量化如下(一切都已经是float):

__m128 r = _mm_mul_ps(_mm_set_ps(ny, nx, ny, nx),
                      _mm_set_ps(ny, nx, m_Ly, m_Lx));
__declspec(align(16)) int asInt[4] = {
    _mm_extract_ps(r,0), _mm_extract_ps(r,1),
    _mm_extract_ps(r,2), _mm_extract_ps(r,3)
};
float (&res)[4] = reinterpret_cast<float (&)[4]>(asInt);
result = (res[0] + res[1] + m_Lz) / sqrt(res[2] + res[3] + 1);

结果正确;但是,我的基准测试显示矢量化版本较慢

  • 非矢量化版本需要 3750 毫秒
  • 矢量化版本需要 4050 毫秒
  • 直接将result 设置为0(并完全删除这部分代码)将整个过程减少到2500 ms

鉴于矢量化版本仅包含 一个 组 SSE 乘法(而不是四个单独的 FPU 乘法),为什么它更慢? FPU 确实比 SSE 快,还是这里有一个混淆变量?

(我使用的是移动 Core i5。)

【问题讨论】:

  • 我已经有一段时间没有看到关于 SO 的 SSE 问题了。我想每个人都从假期回来。 :)

标签: c++ optimization sse vectorization simd


【解决方案1】:

您花费大量时间使用 _mm_set_ps_mm_extract_ps 将标量值移入/移出 SSE 寄存器 - 这会生成大量指令,其执行时间将远远超过使用 _mm_mul_ps 带来的任何好处.查看生成的汇编输出,了解除了单个 MULPS 指令之外还生成了多少代码。

要正确矢量化,您需要使用 128 位 SSE 加载和存储 (_mm_load_ps/_mm_store_ps),然后使用 SSE shuffle 指令在需要的地方移动寄存器内的元素。

还有一点需要注意 - 现代 CPU,如 Core i5、Core i7,有两个标量 FPU,每个时钟可以发出 2 个浮点乘法。因此,SSE 对单精度浮点的潜在好处最多只有 2 倍。如果您有过多的“管家”指令,很容易失去大部分/全部这 2 倍的好处,就像这里的情况一样。

【讨论】:

  • 呵呵,没想到在寄存器之间移动值这么慢!我实际上是在尝试避免内存操作。很高兴知道,非常感谢! :) +1
  • @Mehrdad 这很慢,因为它在不同域的寄存器之间移动(SSE-FP 与通用寄存器)。跨域数据移动通常会有额外的 1-2 个周期惩罚。
  • @Mysticial:哦,好点——我完全忘记了 FPU 寄存器与通用寄存器有很大不同。 :)
【解决方案2】:

我认为处理器有时间在加载下一个值时使用 FPU 计算第一次乘法。 SSE 必须先加载所有值。

【讨论】:

    【解决方案3】:

    有几个问题:

    1. 您不会看到在此类操作中使用 SSE 指令有太多好处,因为 SSE 指令在并行操作(即同时乘以多个值)时应该更好。你所做的是滥用 SSE
    2. 不要设置值,使用指向数组中第一个值的指针,但是你的值不在数组中
    3. 不要提取值并将其复制到数组中。这也是对 SSE 的滥用。结果应该在一个数组中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多