SSE指令集入門

原文地址: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++

  1. void CSSETestDlg::ComputeArrayCPlusPlus(  
  2.           float* pArray1,                   // [in] first source array  
  3.           float* pArray2,                   // [in] second source array  
  4.           float* pResult,                   // [out] result array  
  5.           int nSize)                        // [in] size of all arrays  
  6. {  
  7.   
  8.     int i;  
  9.   
  10.     float* pSource1 = pArray1;  
  11.     float* pSource2 = pArray2;  
  12.     float* pDest = pResult;  
  13.   
  14.     for ( i = 0; i < nSize; i++ )  
  15.     {  
  16.         *pDest = (float)sqrt((*pSource1) * (*pSource1) + (*pSource2)  
  17.                  * (*pSource2)) + 0.5f;  
  18.   
  19.         pSource1++;  
  20.         pSource2++;  
  21.         pDest++;  
  22.     }  
  23. }  


使用SSE內嵌原語

  1. void CSSETestDlg::ComputeArrayCPlusPlusSSE(  
  2.           float* pArray1,                   // [in] first source array  
  3.           float* pArray2,                   // [in] second source array  
  4.           float* pResult,                   // [out] result array  
  5.           int nSize)                        // [in] size of all arrays  
  6. {  
  7.     int nLoop = nSize/ 4;  
  8.   
  9.     __m128 m1, m2, m3, m4;  
  10.   
  11.     __m128* pSrc1 = (__m128*) pArray1;  
  12.     __m128* pSrc2 = (__m128*) pArray2;  
  13.     __m128* pDest = (__m128*) pResult;  
  14.   
  15.   
  16.     __m128 m0_5 = _mm_set_ps1(0.5f);        // m0_5[0, 1, 2, 3] = 0.5  
  17.   
  18.     for ( int i = 0; i < nLoop; i++ )  
  19.     {  
  20.         m1 = _mm_mul_ps(*pSrc1, *pSrc1);        // m1 = *pSrc1 * *pSrc1  
  21.         m2 = _mm_mul_ps(*pSrc2, *pSrc2);        // m2 = *pSrc2 * *pSrc2  
  22.         m3 = _mm_add_ps(m1, m2);                // m3 = m1 + m2  
  23.         m4 = _mm_sqrt_ps(m3);                   // m4 = sqrt(m3)  
  24.         *pDest = _mm_add_ps(m4, m0_5);          // *pDest = m4 + 0.5  
  25.           
  26.         pSrc1++;  
  27.         pSrc2++;  
  28.         pDest++;  
  29.     }  
  30. }  


使用SSE彙編

  1. void CSSETestDlg::ComputeArrayAssemblySSE(  
  2.           float* pArray1,                   // [輸入] 源數組1  
  3.           float* pArray2,                   // [輸入] 源數組2  
  4.           float* pResult,                   // [輸出] 用來存放結果的數組  
  5.           int nSize)                        // [輸入] 數組的大小  
  6. {  
  7.     int nLoop = nSize/4;  
  8.     float f = 0.5f;  
  9.   
  10.     _asm  
  11.     {  
  12.         movss   xmm2, f                         // xmm2[0] = 0.5  
  13.         shufps  xmm2, xmm2, 0                   // xmm2[1, 2, 3] = xmm2[0]  
  14.   
  15.         mov         esi, pArray1                // 輸入的源數組1的地址送往esi  
  16.         mov         edx, pArray2                // 輸入的源數組2的地址送往edx  
  17.   
  18.         mov         edi, pResult                // 輸出結果數組的地址保存在edi  
  19.         mov         ecx, nLoop                  //循環次數送往ecx  
  20.   
  21. start_loop:  
  22.         movaps      xmm0, [esi]                 // xmm0 = [esi]  
  23.         mulps       xmm0, xmm0                  // xmm0 = xmm0 * xmm0  
  24.   
  25.         movaps      xmm1, [edx]                 // xmm1 = [edx]  
  26.         mulps       xmm1, xmm1                  // xmm1 = xmm1 * xmm1  
  27.   
  28.         addps       xmm0, xmm1                  // xmm0 = xmm0 + xmm1  
  29.         sqrtps      xmm0, xmm0                  // xmm0 = sqrt(xmm0)  
  30.   
  31.         addps       xmm0, xmm2                  // xmm0 = xmm1 + xmm2  
  32.   
  33.         movaps      [edi], xmm0                 // [edi] = xmm0  
  34.   
  35.         add         esi, 16                     // esi += 16  
  36.         add         edx, 16                     // edx += 16  
  37.         add         edi, 16                     // edi += 16  
  38.   
  39.         dec         ecx                         // ecx--  
  40.         jnz         start_loop                //如果不爲0則轉向start_loop  
  41.     }  
  42. }  


在信號處理中的實際應用(sse2):

獲得信號能量


  1. /* 
  2. * Compute Energy of a complex signal vector, removing the DC component!  
  3. * input  : points to vector 
  4. * length : length of vector in complex samples 
  5. */  
  6.   
  7. #define shift 4  
  8. #define shift_DC 0  
  9.   
  10. int signal_energy(int *input, unsigned int length)  
  11. {  
  12.     int i;  
  13.     int temp, temp2;  
  14.     register __m64 mm0, mm1, mm2, mm3;  
  15.     __m64 *in;  
  16.   
  17.     in = (__m64 *)input;  
  18.   
  19.     mm0 = _m_pxor(mm0,mm0);  
  20.     mm3 = _m_pxor(mm3,mm3);  
  21.   
  22.     for (i = 0; i < length >> 1; i++) {  
  23.         mm1 = in[i];  
  24.         mm2 = mm1;  
  25.         mm1 = _m_pmaddwd(mm1, mm1);  
  26.         mm1 = _m_psradi(mm1, shift);  
  27.         mm0 = _m_paddd(mm0, mm1);  
  28.         mm2 = _m_psrawi(mm2, shift_DC);  
  29.         mm3 = _m_paddw(mm3, mm2);  
  30.     }  
  31.   
  32.     mm1 = mm0;  
  33.     mm0 = _m_psrlqi(mm0, 32);  
  34.     mm0 = _m_paddd(mm0, mm1);  
  35.     temp = _m_to_int(mm0);  
  36.     temp /= length;  
  37.     temp <<= shift;   
  38.   
  39.     /*now remove the DC component*/  
  40.     mm2 = _m_psrlqi(mm3, 32);  
  41.     mm2 = _m_paddw(mm2, mm3);  
  42.     mm2 = _m_pmaddwd(mm2, mm2);  
  43.     temp2 = _m_to_int(mm2);  
  44.     temp2 /= (length * length);  
  45.     temp2 <<= (2 * shift_DC);  
  46.     temp -= temp2;  
  47.     _mm_empty();  
  48.     _m_empty();  
  49.   
  50.     return((temp > 0) ? temp : 1);  
  51. }  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章