5

考虑使用 SSE 的这两个函数:

#include <xmmintrin.h>

int ftrunc1(float f) {
    return _mm_cvttss_si32(_mm_set1_ps(f));
}

int ftrunc2(float f) {
    return _mm_cvttss_si32(_mm_set_ss(f));
}

对于任何输入,两者的行为完全相同。但是汇编器的输出是不同的:

ftrunc1:
    pushl   %ebp
    movl    %esp, %ebp
    cvttss2si   8(%ebp), %eax
    leave
    ret

ftrunc2:
    pushl   %ebp
    movl    %esp, %ebp
    movss   8(%ebp), %xmm0
    cvttss2si   %xmm0, %eax
    leave
    ret

也就是说,额外ftrunc2使用一条指令!movss

这是正常的吗?有关系吗?当您只需要设置底部元素时,应该_mm_set1_ps始终首选?_mm_set_ss


使用的编译器是 GCC 4.5.2,带有-O3 -msse.

4

2 回答 2

5

_mm_set_ss直接映射到汇编指令 ( movss)。但_mm_set1_ps没有。

根据我在 GCC、MSVC 和 ICC 上看到的情况:

将一对一映射到汇编指令的 SSE 内在函数通常被“按原样”处理——一个黑盒子。所以编译器只会优化适用于整个指令本身的优化。但它不会尝试对单个向量元素进行任何需要数据流/依赖性分析的优化。

和内在函数不映射到单个指令,并且大多数编译器都有特殊情况处理_mm_set1_ps_mm_set_ps从我所见,上面列出的所有三个编译器尝试对各个元素执行数据流分析优化。


当你把它们放在一起时,第二个例子离开了,movss因为编译器没有意识到前 3 个元素无关紧要。(它没有试图“打开”_mm_set_ss内在。)

于 2012-08-30T19:14:42.813 回答
0

您遇到了窥孔优化器的怪癖。由于某种原因,在第一种情况下它发现它可以折叠mov成,cvttss2si而在第二种情况下它失败了。问题是,这有关系吗?额外的移动指令几乎免费——它在指令流中占用额外的 4 个字节和一个额外的解码槽,但两个序列都需要相同数量的执行槽和相同数量的加载/存储槽(这通常很重要)。唯一潜在的症结是 ifetch 的 4 个额外字节——但由于 ftrunc1 使用 10 个字节,而 ftrunc2 使用 14 个字节,因此两者都适合单个高速缓存行,因此您不会看到任何差异。为了最大限度地减少这种开销,我会更关心不需要的 %ebp 垃圾(您是否使用 -fno-omit-frame-pointer 进行编译? - 我虽然 -O3 默认包含 -fomit-frame-pointer )。通过内联此函数,您会做得更好,这可能会完全改变窥视孔优化器看到的内容,因此可能会使其在任何一种情况下都能更好地工作(或者甚至反转它工作得更好的情况)——那里'

最重要的是,两者之间不太可能有任何可测量的速度差异......

于 2012-08-30T18:38:11.333 回答