原文地址:http://blog.csdn.net/wensishuai/article/details/8439418
Intel公司的单指令多数据流式扩展(SSE,Streaming SIMD Extensions)技术能够有效增强CPU浮点运算的能力。Visual Studio .NET 2003提供了对SSE指令集的编程支持,从而允许用户在C++代码中不用编写汇编代码就可直接使用SSE指令的功能。MSDN中有关SSE技术的主题[1]有可能会使不熟悉使用SSE汇编指令编程的初学者感到困惑,但是在阅读MSDN有关文档的同时,参考一下Intel软件说明书(Intel Software manuals)[2]会使你更清楚地理解使用SSE指令编程的要点。
SIMD(single-instruction, multiple-data)是一种使用单道指令处理多道数据流的CPU执行模式,即在一个CPU指令执行周期内用一道指令完成处理多个数据的操作。考虑一下下面这个任务:计算一个很长的浮点型数组中每一个元素的平方根。实现这个任务的算法可以这样写:
for each f in array //对数组中的每一个元素
f = sqrt(f) //计算它的平方根
为了了解实现的细节,我们把上面的代码这样写:
for each f in array
{
把f从内存加载到浮点寄存器
计算平方根
再把计算结果从寄存器中取出放入内存
}
具有Intel SSE指令集支持的处理器有8个128位的寄存器,每一个寄存器可以存放4个(32位)单精度的浮点数。SSE同时提供了一个指令集,其中的指令可以允许把浮点数加载到这些128位的寄存器之中,这些数就可以在这些寄存器中进行算术逻辑运算,然后把结果放回内存。采用SSE技术后,算法可以写成下面的样子:
for each 4 members in array //对数组中的每4个元素
{
把数组中的这4个数加载到一个128位的SSE寄存器中
在一个CPU指令执行周期中完成计算这4个数的平方根的操作
把所得的4个结果取出写入内存
}
下面是一个演示的例子
使用纯C++
- void CSSETestDlg::ComputeArrayCPlusPlus(
- float* pArray1, // [in] first source array
- float* pArray2, // [in] second source array
- float* pResult, // [out] result array
- int nSize) // [in] size of all arrays
- {
- int i;
- float* pSource1 = pArray1;
- float* pSource2 = pArray2;
- float* pDest = pResult;
- for ( i = 0; i < nSize; i++ )
- {
- *pDest = (float)sqrt((*pSource1) * (*pSource1) + (*pSource2)
- * (*pSource2)) + 0.5f;
- pSource1++;
- pSource2++;
- pDest++;
- }
- }
使用SSE内嵌原语
- void CSSETestDlg::ComputeArrayCPlusPlusSSE(
- float* pArray1, // [in] first source array
- float* pArray2, // [in] second source array
- float* pResult, // [out] result array
- int nSize) // [in] size of all arrays
- {
- int nLoop = nSize/ 4;
- __m128 m1, m2, m3, m4;
- __m128* pSrc1 = (__m128*) pArray1;
- __m128* pSrc2 = (__m128*) pArray2;
- __m128* pDest = (__m128*) pResult;
- __m128 m0_5 = _mm_set_ps1(0.5f); // m0_5[0, 1, 2, 3] = 0.5
- for ( int i = 0; i < nLoop; i++ )
- {
- m1 = _mm_mul_ps(*pSrc1, *pSrc1); // m1 = *pSrc1 * *pSrc1
- m2 = _mm_mul_ps(*pSrc2, *pSrc2); // m2 = *pSrc2 * *pSrc2
- m3 = _mm_add_ps(m1, m2); // m3 = m1 + m2
- m4 = _mm_sqrt_ps(m3); // m4 = sqrt(m3)
- *pDest = _mm_add_ps(m4, m0_5); // *pDest = m4 + 0.5
- pSrc1++;
- pSrc2++;
- pDest++;
- }
- }
使用SSE汇编
- void CSSETestDlg::ComputeArrayAssemblySSE(
- float* pArray1, // [输入] 源数组1
- float* pArray2, // [输入] 源数组2
- float* pResult, // [输出] 用来存放结果的数组
- int nSize) // [输入] 数组的大小
- {
- int nLoop = nSize/4;
- float f = 0.5f;
- _asm
- {
- movss xmm2, f // xmm2[0] = 0.5
- shufps xmm2, xmm2, 0 // xmm2[1, 2, 3] = xmm2[0]
- mov esi, pArray1 // 输入的源数组1的地址送往esi
- mov edx, pArray2 // 输入的源数组2的地址送往edx
- mov edi, pResult // 输出结果数组的地址保存在edi
- mov ecx, nLoop //循环次数送往ecx
- start_loop:
- movaps xmm0, [esi] // xmm0 = [esi]
- mulps xmm0, xmm0 // xmm0 = xmm0 * xmm0
- movaps xmm1, [edx] // xmm1 = [edx]
- mulps xmm1, xmm1 // xmm1 = xmm1 * xmm1
- addps xmm0, xmm1 // xmm0 = xmm0 + xmm1
- sqrtps xmm0, xmm0 // xmm0 = sqrt(xmm0)
- addps xmm0, xmm2 // xmm0 = xmm1 + xmm2
- movaps [edi], xmm0 // [edi] = xmm0
- add esi, 16 // esi += 16
- add edx, 16 // edx += 16
- add edi, 16 // edi += 16
- dec ecx // ecx--
- jnz start_loop //如果不为0则转向start_loop
- }
- }
在信号处理中的实际应用(sse2):
获得信号能量
- /*
- * Compute Energy of a complex signal vector, removing the DC component!
- * input : points to vector
- * length : length of vector in complex samples
- */
- #define shift 4
- #define shift_DC 0
- int signal_energy(int *input, unsigned int length)
- {
- int i;
- int temp, temp2;
- register __m64 mm0, mm1, mm2, mm3;
- __m64 *in;
- in = (__m64 *)input;
- mm0 = _m_pxor(mm0,mm0);
- mm3 = _m_pxor(mm3,mm3);
- for (i = 0; i < length >> 1; i++) {
- mm1 = in[i];
- mm2 = mm1;
- mm1 = _m_pmaddwd(mm1, mm1);
- mm1 = _m_psradi(mm1, shift);
- mm0 = _m_paddd(mm0, mm1);
- mm2 = _m_psrawi(mm2, shift_DC);
- mm3 = _m_paddw(mm3, mm2);
- }
- mm1 = mm0;
- mm0 = _m_psrlqi(mm0, 32);
- mm0 = _m_paddd(mm0, mm1);
- temp = _m_to_int(mm0);
- temp /= length;
- temp <<= shift;
- /*now remove the DC component*/
- mm2 = _m_psrlqi(mm3, 32);
- mm2 = _m_paddw(mm2, mm3);
- mm2 = _m_pmaddwd(mm2, mm2);
- temp2 = _m_to_int(mm2);
- temp2 /= (length * length);
- temp2 <<= (2 * shift_DC);
- temp -= temp2;
- _mm_empty();
- _m_empty();
- return((temp > 0) ? temp : 1);
- }