阅读this interesting article on the results of intrinsic-guided optimization of SSE code in different C++ compilers我决定自己做一个测试,特别是因为这个帖子已经过了几年了。我使用了MSVC,它在帖子作者的测试中做得很差(虽然在VS 2010版本中),并决定坚持一个非常基本的场景:将一些值打包到XMM寄存器中,并进行简单操作,如添加。在文章中,_mm_set_ps翻译成标举的一个奇怪的序列,并解压缩指令,让我们看看:如何改进编译器对我的SSE内部函数的处理?
int _tmain(int argc, _TCHAR* argv[])
{
__m128 foo = _mm_set_ps(1.0f, 2.0f, 3.0f, 4.0f);
__m128 bar = _mm_set_ps(5.0f, 6.0f, 7.0f, 8.0f);
__m128 ret = _mm_add_ps(foo, bar);
// need to do something so vars won't be optimized out in Release
float *f = (float *)(&ret);
for (int i = 0; i < 4; i++)
{
cout << "f[" << i << "] = " << f[i] << endl;
}
}
接下来,我编译和运行这个调试器里面,看着拆卸:
调试:
__m128 foo = _mm_set_ps(1.0f,2.0f,3.0f,4.0f);
00B814F0 MOVAPS XMM0,xmmword PTR DS:[0B87840h]
00B814F7 MOVAPS xmmword PTR [EBP-190H],XMM0
00B814FE MOVAPS XMM0,xmmword PTR [EBP-190H]
00B81505 MOVAPS xmmword的ptr [富],XMM0
__m128 bar = _mm_set_ps(5.0f,6.0f,7.0f,8.0f);
00B81509 MOVAPS XMM0,xmmword PTR DS:[0B87850h]
00B81510 MOVAPS xmmword PTR [EBP-170H],XMM0
00B81517 MOVAPS XMM0,xmmword PTR [EBP-170H]
00B8151E MOVAPS xmmword的ptr [巴],XMM0
__m128 ret = _mm_add_ps(foo,bar);
00B81522 MOVAPS XMM0,xmmword的ptr [巴]
00B81526 MOVAPS xmm1中,xmmword的ptr [富]
00B8152A ADDPS xmm1中,XMM0
00B8152D MOVAPS xmmword PTR [EBP-150H],xmm1中
00B81534 MOVAPS XMM0,xmmword PTR [EBP-150H]
00B8153B MOVAPS xmmword的ptr [RET],XMM0
心乱如麻;为什么将xmmword放入__m128需要四个MOVAPS?首先,它将数据放入xmm0中(我认为这是四个浮点值存储在某处的文字,不知道如何查看它),然后将xmm0复制到某处由ebp指向的位置和一个偏移量,仅将其复制回来到xmm0(?),最后到应该存储变量的位置。为什么这么多工作?
发布: 这一次,我期待编译器可避免储存xmmword在内存中的所有,只是把一个在XMM0,其他xmm1中,在内存中做一个ADDPS,把结果和用它做。相反,我得到:
__m128 foo = _mm_set_ps(1.0f,2.0f,3.0f,4.0f);
__m128 bar = _mm_set_ps(5.0f,6.0f,7.0f,8.0f);
__m128 ret = _mm_add_ps(foo,bar);
003E1009 MOVAPS XMM0,xmmword PTR DS:[3E2130h]
003E1010推ESI
003E1011 MOVAPS xmmword的ptr [ESP + 10H],XMM0
显然,不需要ADDPS。我猜测编译器注意到这两个xmmwords是编译时常量,所以它只是添加了它们,将结果作为文字输入到代码中?奇怪的推动可能与随后的for循环有关,因为esi被用作循环计数器,据我所知。尽管如此,为什么不将数据段中的预先计算的文字放入xmm0,然后放入局部变量(esp + 10h),为什么不直接使用文字?总而言之,Debug版本比我预期的更愚蠢(或者我可能没有收到什么东西),而发布版本却意想不到。任何意见解释这种行为将不胜感激。谢谢。
编辑:的答案是很有启发性,但我还是想知道如果有什么我可以做些什么来改善编译器的输出,这就是为什么我从询问到的这种解释改变的问题目前的形式。
例如,才有可能以某种方式引导编译器富和酒吧不存储在内存中(因为我不添加后需要他们),只需将它们装入xmmN寄存器,并让他们那里?可能ret呢?引用文章的作者说,MSVC只是“按照它所告诉的那样做”。任何方式来改善(读:避免内存传输)代码,而不明确写一个__asm块?谢谢。
一个非常好的解释! – us2012 2013-02-14 14:33:53
谢谢@Hans,您是否也会好好处理Q上的编辑?谢谢。 – neuviemeporte 2013-02-14 15:18:43
要做任何事情来改善它都没有意义。只需构建您的项目的发布版本即可完成。在调试版本中打开优化器只会让调试更加困难,调试版本的目的是让它更容易*。如果你真的想,你可以帮助你了解它有多难。 – 2013-02-14 15:27:41