• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            基于SSE指令集的程序設(shè)計簡介

            基于SSE指令集的程序設(shè)計簡介

            作者:Alex Farber
            出處:http://www.codeproject.com/cpp/sseintro.asp

            SSE技術(shù)簡介


            Intel公司的單指令多數(shù)據(jù)流式擴展(SSE,Streaming SIMD Extensions)技術(shù)能夠有效增強CPU浮點運算的能力。Visual Studio .NET 2003提供了對SSE指令集的編程支持,從而允許用戶在C++代碼中不用編寫匯編代碼就可直接使用SSE指令的功能。MSDN中有關(guān)SSE技術(shù)的主題[1]有可能會使不熟悉使用SSE匯編指令編程的初學(xué)者感到困惑,但是在閱讀MSDN有關(guān)文檔的同時,參考一下Intel軟件說明書(Intel Software manuals)[2]會使你更清楚地理解使用SSE指令編程的要點。

            SIMD(single-instruction, multiple-data)是一種使用單道指令處理多道數(shù)據(jù)流的CPU執(zhí)行模式,即在一個CPU指令執(zhí)行周期內(nèi)用一道指令完成處理多個數(shù)據(jù)的操作。考慮一下下面這個任務(wù):計算一個很長的浮點型數(shù)組中每一個元素的平方根。實現(xiàn)這個任務(wù)的算法可以這樣寫:

            for each  f in array        //對數(shù)組中的每一個元素
                f = sqrt(f)             //計算它的平方根

            為了了解實現(xiàn)的細(xì)節(jié),我們把上面的代碼這樣寫:

            for each  f in array
            {
                把f從內(nèi)存加載到浮點寄存器
                計算平方根
                再把計算結(jié)果從寄存器中取出放入內(nèi)存
            }

            具有Intel SSE指令集支持的處理器有8個128位的寄存器,每一個寄存器可以存放4個(32位)單精度的浮點數(shù)。SSE同時提供了一個指令集,其中的指令可以允許把浮點數(shù)加載到這些128位的寄存器之中,這些數(shù)就可以在這些寄存器中進行算術(shù)邏輯運算,然后把結(jié)果放回內(nèi)存。采用SSE技術(shù)后,算法可以寫成下面的樣子:

            for each  4 members in array  //對數(shù)組中的每4個元素
            {
                把數(shù)組中的這4個數(shù)加載到一個128位的SSE寄存器中
                在一個CPU指令執(zhí)行周期中完成計算這4個數(shù)的平方根的操作
                把所得的4個結(jié)果取出寫入內(nèi)存
            }
             
            C++編程人員在使用SSE指令函數(shù)編程時不必關(guān)心這些128位的寄存器,你可以使用128位的數(shù)據(jù)類型“__m128”和一系列C++函數(shù)來實現(xiàn)這些算術(shù)和邏輯操作,而決定程序使用哪個SSE寄存器以及代碼優(yōu)化是C++編譯器的任務(wù)。當(dāng)需要對很長的浮點數(shù)數(shù)組中的元素進行處理的時候,SSE技術(shù)確實是一種很高效的方法。


            SSE程序設(shè)計詳細(xì)介紹

            包含的頭文件:

            所有的SSE指令函數(shù)和__m128數(shù)據(jù)類型都在xmmintrin.h文件中定義:
            #include <xmmintrin.h>
            因為程序中用到的SSE處理器指令是由編譯器決定,所以它并沒有相關(guān)的.lib庫文件。

            數(shù)據(jù)分組(Data Alignment)

            由SSE指令處理的每一個浮點數(shù)數(shù)組必須把其中需要處理的數(shù)每16個字節(jié)(128位二進制)分為一組。一個靜態(tài)數(shù)組(static array)可由__declspec(align(16))關(guān)鍵字聲明:

            __declspec(align(16)) float m_fArray[ARRAY_SIZE];

            動態(tài)數(shù)組(dynamic array)可由_aligned_malloc函數(shù)為其分配空間:
            m_fArray = (float*) _aligned_malloc(ARRAY_SIZE * sizeof(float), 16);

            由_aligned_malloc函數(shù)分配空間的動態(tài)數(shù)組可以由_aligned_free函數(shù)釋放其占用的空間:
            _aligned_free(m_fArray);

            __m128 數(shù)據(jù)類型

            該數(shù)據(jù)類型的變量可用做SSE指令的操作數(shù),它們不能被用戶指令直接存取。_m128類型的變量被自動分配為16個字節(jié)的字長。

            CPU對SSE指令集的支持

            如果你的CPU能夠具有了SSE指令集,你就可以使用Visual Studio .NET 2003提供的對SSE指令集支持的C++函數(shù)庫了,你可以查看MSDN中的一個Visual C++ CPUID的例子[4],它可以幫你檢測你的CPU是否支持SSE、MMX指令集或其它的CPU功能。


            編程實例
            以下講解了SSE技術(shù)在Visual Studio .NET 2003下的應(yīng)用實例,你可以在
            http://www.codeproject.com/cpp/sseintro/SSE_src.zip下載示例程序壓縮包。該壓縮包中含有兩個項目,這兩個項目是基于微軟基本類庫(MFC)建立的Visual C++.NET項目,你也可以按照下面的講解建立這兩個項目。

            SSETest 示例項目

            SSETest項目是一個基于對話框的應(yīng)用程序,它用到了三個浮點數(shù)組參與運算:

            fResult[i] = sqrt( fSource1[i]*fSource1[i] + fSource2[i]*fSource2[i] ) + 0.5

            其中i = 0, 1, 2 ... ARRAY_SIZE-1

            其中ARRAY_SIZE被定義為30000。數(shù)據(jù)源數(shù)組(Source數(shù)組)通過使用sin和cos函數(shù)給它賦值,我們用Kris Jearakul開發(fā)的瀑布狀圖表控件(Waterfall chart control)[3] 來顯示參與計算的源數(shù)組和結(jié)果數(shù)組。計算所需的時間(以毫秒ms為單位)在對話框中顯示出來。我們使用三種不同的途徑來完成計算:

            純C++代碼;
            使用SSE指令函數(shù)的C++代碼;
            包含SSE匯編指令的代碼。


            純C++代碼:

             

            void CSSETestDlg::ComputeArrayCPlusPlus(
                      
            float* pArray1,                   // [輸入] 源數(shù)組1
                      float* pArray2,                   // [輸入] 源數(shù)組2
                      float* pResult,                   // [輸出] 用來存放結(jié)果的數(shù)組
                      int nSize)                            // [輸入] 數(shù)組的大小
            {

                
            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特性的C++代碼重寫上面這個函數(shù)。為了查詢使用SSE指令C++函數(shù)的方法,我參考了Intel軟件說明書(Intel Software manuals)中有關(guān)SSE匯編指令的說明,首先我是在第一卷的第九章找到的相關(guān)SSE指令,然后在第二卷找到了這些SSE指令的詳細(xì)說明,這些說明有一部分涉及了與其特性相關(guān)的C++函數(shù)。然后我通過這些SSE指令對應(yīng)的C++函數(shù)查找了MSDN中與其相關(guān)的說明。搜索的結(jié)果見下表:

            實現(xiàn)的功能 對應(yīng)的SSE匯編指令 Visual C++.NET中的SSE函數(shù)
            將4個32位浮點數(shù)放進一個128位的存儲單元。 movss 和 shufps _mm_set_ps1
            將4對32位浮點數(shù)同時進行相乘操作。這4對32位浮點數(shù)來自兩個128位的存儲單元,再把計算結(jié)果(乘積)賦給一個128位的存儲單元。 mulps _mm_mul_ps
            將4對32位浮點數(shù)同時進行相加操作。這4對32位浮點數(shù)來自兩個128位的存儲單元,再把計算結(jié)果(相加之和)賦給一個128位的存儲單元。 addps _mm_add_ps
            對一個128位存儲單元中的4個32位浮點數(shù)同時進行求平方根操作。 sqrtps

            _mm_sqrt_ps

             

            使用Visual C++.NET的 SSE指令函數(shù)的代碼:

             

            void CSSETestDlg::ComputeArrayCPlusPlusSSE(
                      
            float* pArray1,                   // [輸入] 源數(shù)組1
                      float* pArray2,                   // [輸入] 源數(shù)組2
                      float* pResult,                   // [輸出] 用來存放結(jié)果的數(shù)組
                      int nSize)                        // [輸入] 數(shù)組的大小
            {
                
            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匯編指令實現(xiàn)的C++函數(shù)代碼:

             

            void CSSETestDlg::ComputeArrayAssemblySSE(
                      
            float* pArray1,                   // [輸入] 源數(shù)組1
                      float* pArray2,                   // [輸入] 源數(shù)組2
                      float* pResult,                   // [輸出] 用來存放結(jié)果的數(shù)組
                      int nSize)                        // [輸入] 數(shù)組的大小
            {
                
            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                
            // 輸入的源數(shù)組1的地址送往esi
                    mov         edx, pArray2                // 輸入的源數(shù)組2的地址送往edx

                    mov         edi, pResult                
            // 輸出結(jié)果數(shù)組的地址保存在edi
                    mov         ecx, nLoop                  //循環(huán)次數(shù)送往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則轉(zhuǎn)向start_loop
                }

            }

             

            最后,在我的計算機上運行計算測試的結(jié)果:

            純C++代碼計算所用的時間是26 毫秒
            使用SSE的C++ 函數(shù)計算所用的時間是 9 毫秒
            包含SSE匯編指令的C++代碼計算所用的時間是 9 毫秒

            以上的時間結(jié)果是在Release優(yōu)化編譯后執(zhí)行程序得出的。

             

            SSESample 示例項目


            SSESample項目是一個基于對話框的應(yīng)用程序,其中它用下面的浮點數(shù)數(shù)組進行計算:

            fResult[i] = sqrt(fSource[i]*2.8)

            其中i = 0, 1, 2 ... ARRAY_SIZE-1

            這個程序同時計算了數(shù)組中的最大值和最小值。ARRAY_SIZE被定義為100000,數(shù)組中的計算結(jié)果在列表框中顯示出來。其中在我的機子上用下面三種方法計算所需的時間是:
            純C++代碼計算                   6 毫秒
            使用SSE的C++ 函數(shù)計算     3 毫秒
            使用SSE匯編指令計算         2 毫秒

            大家看到,使用SSE匯編指令計算的結(jié)果會好一些,因為使用了效率增強了的SSX寄存器組。但是在通常情況下,使用SSE的C++ 函數(shù)計算會比匯編代碼計算的效率更高一些,因為C++編譯器的優(yōu)化后的代碼有很高的運算效率,若要使匯編代碼比優(yōu)化后的代碼運算效率更高,這通常是很難做到的。

            純C++代碼:

             

            // 輸入: m_fInitialArray
            // 輸出: m_fResultArray, m_fMin, m_fMax
            void CSSESampleDlg::OnBnClickedButtonCplusplus()
            {
                m_fMin 
            = FLT_MAX;
                m_fMax 
            = FLT_MIN;

                
            int i;

                
            for ( i = 0; i < ARRAY_SIZE; i++ )
                
            {
                    m_fResultArray[i] 
            = sqrt(m_fInitialArray[i]  * 2.8f);

                    
            if ( m_fResultArray[i] < m_fMin )
                        m_fMin 
            = m_fResultArray[i];

                    
            if ( m_fResultArray[i] > m_fMax )
                        m_fMax 
            = m_fResultArray[i];
                }

            }

             

             

            使用Visual C++.NET的 SSE指令函數(shù)的代碼:

             

            // 輸入: m_fInitialArray
            // 輸出: m_fResultArray, m_fMin, m_fMax
            void CSSESampleDlg::OnBnClickedButtonSseC()
            {
                __m128 coeff 
            = _mm_set_ps1(2.8f);      // coeff[0, 1, 2, 3] = 2.8
                __m128 tmp;

                __m128 min128 
            = _mm_set_ps1(FLT_MAX);  // min128[0, 1, 2, 3] = FLT_MAX
                __m128 max128 = _mm_set_ps1(FLT_MIN);  // max128[0, 1, 2, 3] = FLT_MIN

                __m128
            * pSource = (__m128*) m_fInitialArray;
                __m128
            * pDest = (__m128*) m_fResultArray;

                
            for ( int i = 0; i < ARRAY_SIZE/4; i++ )
                
            {
                    tmp 
            = _mm_mul_ps(*pSource, coeff);      // tmp = *pSource * coeff
                    *pDest = _mm_sqrt_ps(tmp);              // *pDest = sqrt(tmp)

                    min128 
            =  _mm_min_ps(*pDest, min128);
                    max128 
            =  _mm_max_ps(*pDest, max128);

                    pSource
            ++;
                    pDest
            ++;
                }


                
            // 計算max128的最大值和min128的最小值
                union u
                
            {
                    __m128 m;
                    
            float f[4];
                }
             x;

                x.m 
            = min128;
                m_fMin 
            = min(x.f[0], min(x.f[1], min(x.f[2], x.f[3])));

                x.m 
            = max128;
                m_fMax 
            = max(x.f[0], max(x.f[1], max(x.f[2], x.f[3])));
            }


             

             

            使用SSE匯編指令的C++函數(shù)代碼:

            // 輸入: m_fInitialArray
            // 輸出: m_fResultArray, m_fMin, m_fMax
            void CSSESampleDlg::OnBnClickedButtonSseAssembly()
            {
               
                
            float* pIn = m_fInitialArray;
                
            float* pOut = m_fResultArray;

                
            float f = 2.8f;
                
            float flt_min = FLT_MIN;
                
            float flt_max = FLT_MAX;

                __m128 min128;
                __m128 max128;

                
            // 使用以下的附加寄存器:xmm2、xmm3、xmm4:
                
            // xmm2 – 相乘系數(shù)
                
            // xmm3 – 最小值
                
            // xmm4 – 最大值

                _asm
                
            {
                    movss   xmm2, f                         
            // xmm2[0] = 2.8
                    shufps  xmm2, xmm2, 0                   // xmm2[1, 2, 3] = xmm2[0]

                    movss   xmm3, flt_max                   
            // xmm3 = FLT_MAX
                    shufps  xmm3, xmm3, 0                   // xmm3[1, 2, 3] = xmm3[0]

                    movss   xmm4, flt_min                   
            // xmm4 = FLT_MIN
                    shufps  xmm4, xmm4, 0                   // xmm3[1, 2, 3] = xmm3[0]

                    mov         esi, pIn                    
            // 輸入數(shù)組的地址送往esi
                    mov         edi, pOut                   // 輸出數(shù)組的地址送往edi
                    mov         ecx, ARRAY_SIZE/4           // 循環(huán)計數(shù)器初始化

            start_loop:
                    movaps      xmm1, [esi]                 
            // xmm1 = [esi]
                    mulps       xmm1, xmm2                  // xmm1 = xmm1 * xmm2
                    sqrtps      xmm1, xmm1                  // xmm1 = sqrt(xmm1)
                    movaps      [edi], xmm1                 // [edi] = xmm1

                    minps       xmm3, xmm1
                    maxps       xmm4, xmm1

                    add         esi, 
            16
                    add         edi, 
            16

                    dec         ecx
                    jnz         start_loop


                    movaps      min128, xmm3
                    movaps      max128, xmm4
                }


                union u
                
            {
                    __m128 m;
                    
            float f[4];
                }
             x;

                x.m 
            = min128;
                m_fMin 
            = min(x.f[0], min(x.f[1], min(x.f[2], x.f[3])));

                x.m 
            = max128;
                m_fMax 
            = max(x.f[0], max(x.f[1], max(x.f[2], x.f[3])));

            }


             


            參考文檔:

            [1]MSDN, SSE技術(shù)主題:
            http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vcrefstreamingsimdextensions.asp

            [2]Intel軟件說明書(Intel Software manuals):
            http://developer.intel.com/design/archives/processors/mmx/index.htm

            [3] Kris Jearakul的瀑布狀圖表控件:http://www.codeguru.com/controls/Waterfall.shtml

            [4] Microsoft Visual C++ CPUID示例:
            http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcsample/html/vcsamcpuiddeterminecpucapabilities.asp

            [5] Matt Pietrek在Microsoft Systems Journal 1998年2月刊上的評論文章:
            http://www.microsoft.com/msj/0298/hood0298.aspx

            posted on 2008-05-07 12:08 楊粼波 閱讀(906) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            中文国产成人精品久久亚洲精品AⅤ无码精品 | 日本精品久久久久中文字幕8| 国产成人精品免费久久久久| 久久99精品久久久久久噜噜| 久久久久久精品免费免费自慰| 好属妞这里只有精品久久| 国产精品中文久久久久久久| 91精品国产高清久久久久久国产嫩草 | 久久久久国产精品嫩草影院| 伊人久久大香线蕉av不变影院| 97精品国产97久久久久久免费| 久久国产高清一区二区三区| 久久久噜噜噜久久中文字幕色伊伊| 久久精品国产亚洲AV高清热| 久久精品青青草原伊人| 久久精品国产亚洲AV无码娇色| 久久九九有精品国产23百花影院| 久久久综合香蕉尹人综合网| 久久国产乱子伦精品免费强| 久久久久亚洲爆乳少妇无| 色婷婷久久综合中文久久蜜桃av| 久久人人爽人人爽人人片AV高清 | 国产午夜电影久久| 四虎国产精品免费久久5151| 手机看片久久高清国产日韩| 久久久久亚洲精品天堂久久久久久 | 人妻无码精品久久亚瑟影视| 日韩人妻无码精品久久免费一| 99久久国产主播综合精品| 午夜精品久久久久久中宇| 一本久久知道综合久久| 久久91亚洲人成电影网站| 久久天天躁夜夜躁狠狠| 精品久久久久久无码中文字幕| 亚洲va中文字幕无码久久| 久久精品国产亚洲一区二区三区 | 国产精品无码久久久久| 青青草原精品99久久精品66| 亚洲七七久久精品中文国产| 久久精品一区二区国产| 婷婷伊人久久大香线蕉AV|