SSE2指令优化心得

SSE2可以在一个指令周期完成最多4个指令周期的任务,最理想的情况是比C代码快4倍,但是一般达不到的
因为是处理对象是128位数据,一般1个Int是32位,4倍。

一个教程
http://blog.xuite.net/jiing.deng/jiing/253910

VC的SSE指令帮助
http://msdn.microsoft.com/zh-cn/library/vstudio/84t4h8ys.aspx

一个SIMD库,用SSE,SSE2模拟实现了很多SSE3以上的指令
http://sourceforge.net/projects/sseplus

加载操作
为了将128位数据放到寄存器,我们需要使用_mm_load_si128来加载,注意要加载的数据必须是16个字节对齐的。

_mm_load_si128((__m128i*)coeff);

运算完结果,输出结果的时候,要调用
_mm_store_si128

对齐内存的分配和释放指令指令(Windows)
//16个字节对齐
_aligned_malloc( sizeof(type)*(len), 16 )
_aligned_free ( ptr )

将条件语句转换为SIMD指令,比如
iDst = (iSrc < 0 ? -1: 1);

//同0比较操作完,输出的是-1,0,0,0 和1进行Or操作,得到-1,1,1,1
iDst = _mm_or_si128(_mm_cmplt_epi32(iLevel, _mm_setzero_si128()), _mm_set1_epi32(1));

计算abs,利用abs(x)=max(x, 0-x)来计算
注意当系统中不支持SSE4.1的_mm_max_epi32的指令时,我们可以用SSE2指令来模拟,常见SSEPlus的
ssp_max_epi32_SSE2

iLevel = ssp_max_epi32_SSE2(_mm_sub_epi32(_mm_setzero_si128(), iLevel), iLevel);

乘法溢出的问题。
32位Int和32位int相乘可能会溢出,为了避免溢出,可以使用下面方法,先算第1,3个数据的乘法,保存2个64位整数为一个128位数据,然后再算2,4的,最后可以将4个64位数据合并为一个128位数据,舍掉高位。
__m128i tmp1 = _mm_mul_epu32(a,b); /* mul 2,0*/
__m128i tmp2 = _mm_mul_epu32( _mm_srli_si128(a,4), _mm_srli_si128(b,4)); /* mul 3,1 */
return _mm_unpacklo_epi32(_mm_shuffle_epi32(tmp1, _MM_SHUFFLE (0,0,2,0)), _mm_shuffle_epi32(tmp2, _MM_SHUFFLE (0,0,2,0))); /* shuffle results to [63..0] and pack */

avx2指令的文档,解释

_mm256_permute4x64_epi64
含义是将256位数据分成4个64位的数据,根据掩码映射到新的256位数据上

比如0x00...0x1f 32个byte数据,根据掩码0x01也就是00000001会映射为下面的样子
0x08..0x0f
0x00..0x07
0x00..0x07
0x00..0x07

伪代码
r0=a1
r1=a0
r2=a0
r3=a0

_mm256_broadcastd_epi32指令的含义是将操作数的32位整数,重复8次复制到256位数据的相应位置

相当于
r0= a0
..
r7 = a0

_mm256_hadd_epi16指令含义相当于毗邻的两个16位数据之和

r0= a0+a1
r1= a2+a3
r3= a4+a5
r4= a6+a7
r5= b0+b1
r6= b2+b3
...