《3D.Game.Engine.Programming》书上刚看到一个SHUFPS指令。这个SHUFPS指令类似洗牌功能,这个指令的功能还是比较难理解的。自己多次测试和百度Google后,大致理解这个指令的功能,记录一下。
先借用一篇文章的内容:文章地址
《3D.Game.Engine.Programming》书上的代码例子是SHUFPS XMM0, XMM1, 9Ch,这个9Ch表示的是16进制,等于10011100。刚开始这段文字一直没理解,看了上面这张图,就比较好理解了。
不用寄存器来介绍,用数组来举例:有2个长度为4的数组,从中随机选择数据生成一个新的长度为4的数组,这就是作者一直在说的洗牌功能,但是有个注意点,就是新生成的数组前2个数据是用第一个数组上的数据,后2个数据是用第2个数组上的数据。
但到底是用数组上那个数据呢?00~11,啥玩意啊,好难记啊。经过代码测试,大致理解00~11的功能,类似数组的索引,00转换成10进制是0,01转换成10进制是1,10转换成10进制是2,11转换成10进制是3。再结合上面写的数组例子,就很好知道9Ch这个功能了,就是洗牌的规则。PS:在学汇编的时候,教程上说需要背下16进制到2进制的转换,很难记,大脑反应不过来。
用代码来说明,因为在游戏开发中,4个float的值很容易想到是向量,这边就不采用数组,直接用向量来测试。数据结构还是上一篇的Vector结构。
比如现在有2个4维向量:(1.0f, 2.0f, 3.0f, 4.0f),(5.0f, 6.0f, 7.0f, 8.0f),想生成一个新的向量(1.0f, 4.0f, 6.0f, 7.0f)。这里得说明一下,如果是从这种结构看的话,左边是低位,右边是高位。分析新生成的向量,因为1.0f和4.0f在低位,所以(1.0f, 2.0f, 3.0f, 4.0f)是在源寄存器上,(5.0f, 6.0f, 7.0f, 8.0f)就在目的寄存器上。1.0f在(1.0f, 2.0f, 3.0f, 4.0f)的索引是0(2进制是00),4.0f在(1.0f, 2.0f, 3.0f, 4.0f)的索引是3(2进制是11),6.0f在(5.0f, 6.0f, 7.0f, 8.0f)的索引是1(2进制是01),7.0f在(5.0f, 6.0f, 7.0f, 8.0f)的索引是2(2进制是10)。好,把得到的4个索引数据拼接一下,洗牌规则就是10 01 11 00(左边是高位,右边是低位)。PS:如果我想生成一个新的向量(6.0f, 7.0f, 1.0f, 4.0f),洗牌规则不变,只交换2个寄存器的位置能实现吗?不用代码测试,按照我上面一样去推断。
代码和结果放一下,还好支持2进制,不然放个16进制上去,还得转换下。如果理解上面的内容话,也应该猜到在何时会用到这个功能吧,效率不要太高啊。
代码:
结果:
书上也介绍了SHUFPS指令的一个案例:求4维向量的长度,我就不再做推导了。在汇编代码中,每一步指令后面我都注释了相应的结果。
代码:
float Length()
{
float f = 0.0f;
DWORD currentTime, deltaTime;
float forTimes = 100000000;
currentTime = timeGetTime();
for(int i = 0; i < forTimes; ++i)
f = (float)sqrt(x * x + y * y + z * z);
deltaTime = timeGetTime() - currentTime;
std::cout << "[C++]deltaTime: " << deltaTime << std::endl;
f = 0.0f;
currentTime = timeGetTime();
for (int i = 0; i < forTimes; ++i)
{
w = 0.0f;
__asm
{
mov esi, this;
movups xmm0, [esi] ;xmm0:x, y, z, w
mulps xmm0, xmm0 ;xmm0:x^2, y^2, z^2, w^2
movaps xmm1, xmm0 ;xmm1=xmm0:x^2, y^2, z^2, w^2
shufps xmm1, xmm1, 4Eh ;xmm1:z^2, w^2, x^2, y^2
addps xmm0, xmm1 ;xmm0:x^2+z^2, y^2+w^2, z^2+x^2, w^2+y^2
movaps xmm1, xmm0 ;xmm1=xmm0:x^2+z^2, y^2+w^2,z^2+x^2,w^2+y^2
shufps xmm1, xmm1, 11h ;xmm1:y^2+w^2, x^2+z^2, w^2+y^2, z^2+x^2
addps xmm0, xmm1 ;xmm0:x^2+z^2+y^2+w^2(4个都是)
sqrtss xmm0, xmm0 ;xmm0:sqrt(x^2+z^2+y^2+w^2)
movss f, xmm0 ;
}
w = 1.0f;
}
deltaTime = timeGetTime() - currentTime;
std::cout << "[Assemble]deltaTime: " << deltaTime << std::endl;
return f;
}
100000000多次C++执行的时间和汇编代码执行所需要的时间: