圖形影象處理-之-彩色轉化到灰度的速度優化

pamxy發表於2013-07-10
轉自:http://blog.csdn.net/housisong/article/details/3884368

               圖形影象處理-之-彩色轉化到灰度的速度優化
                   HouSisong@GMail.com  2009.02.08

tag:灰度演算法,速度優化,定點數優化,MMX,SSE,SSE2,CPU快取優化

摘要:
  彩色轉化到灰度的速度優化文章包括圖形影象處理簡單Demo框架和灰度轉換的實
現及其速度優化,並演示其使用SIMD指令集的優化;
   本篇文章將第一次提供完整的可以編譯的影象處理完整專案程式碼;
   (以後會用這個框架逐步改寫以前的圖形影象處理文章)

正文:  
  為了便於討論,這裡只處理32bit的ARGB顏色;程式碼使用C++;使用的編譯器為vc2008;
(經過測試程式碼也可以在DevC++和xcode下編譯通過) 測試使用的CPU為AMD64x2 4200+(2.33G);

速度測試說明:
  只測試記憶體資料到記憶體資料的ARGB32顏色的灰度轉化;
  測試圖片是800*600; fps表示每秒鐘的幀數,值越大表示函式越快;

A: 圖形影象處理簡單Demo框架
   
  我以前寫的圖形影象處理方面的blog文章都沒有完整的可以編譯執行的程式碼,
而僅僅列出了關鍵的核心程式碼;經常有網友看了我的文章,但因為不能實際執行看看,
從而對程式碼的理解不深,也不能把程式碼移植到自己的專案中使用; 所以決定為我的圖形
影象處理系列blog文章建立一個簡單的小型的框架;我把它命名為hGraphic32,
它會盡量的小,演示為主,僅支援ARGB32顏色,能夠載入和儲存bmp圖片檔案,能夠在
多個編譯器和平臺下編譯和執行;
   現在就下載完整專案原始碼吧:  完整專案原始碼 


  <hGraphic32>資料夾裡的檔案說明:
    "hColor32.h"  : 裡面定義了32bitARGB顏色型別Color32,它佔用4位元組,代表一個顏色;
        TPixels32Ref是影象資料區的描述資訊,可以把它理解為一個"指標",指向了Color32構成的畫素區;
        IPixels32Buf是影象資料區介面,用於描述一個影象的緩衝區;
    "hPixels32.h" : 裡面定義了TPixels32類,它實現了IPixels32Buf介面,用於申請和管理一塊記憶體畫素;
    "hStream.h"   : 裡面定義了IInputStream輸入流介面;
        IBufInputStream資料區輸入流介面,繼承自IInputStream;
        TFileInputStream檔案輸入流類,它實現了IBufInputStream介面;
        IOutputStream輸出流介面;
        TFileOutputStream檔案輸出流類,它實現了IOutputStream介面;
     "hBmpFile.h" : 裡面定義了TBmpFile類,它負責載入bmp和儲存bmp;
     "hGraphic32.h" 檔案include了上面的*.h標頭檔案,所以使用的時候,只要#include "hGraphic32.h"就可以了

B: 灰度轉化專案
  所有的轉換和測試程式碼都在"ColorToGray/ColorToGray.cpp"檔案中(帶有main函式的命令列程式);
  "ColorToGray/win_vc/ColorToGray.sln"是windows系統下的vc2008專案檔案(測試的時請設定除錯執行目錄為"..");
  "ColorToGray/win_DevC++/ColorToGray.dev"是windows系統下的DevC++專案檔案;
  "ColorToGray/macosx_xcode/ColorToGray.xcodeproj"是macosx系統下的xcode專案檔案;
  你也可以自己建立專案,包含ColorToGray.cpp檔案和<hGraphic32>資料夾下的所有檔案,就可以編譯了;

C: 灰度轉化公式和程式碼實現
  文章中用的灰度公式: Gray = R*0.299 + G*0.587  + B*0.114;
  
程式碼實現:

  1. //灰度轉換系數  
  2. const double gray_r_coeff=0.299;  
  3. const double gray_g_coeff=0.587;  
  4. const double gray_b_coeff=0.114;  
  5.     //處理一個點  
  6.     must_inline double toGray_float(const Color32& src){  
  7.         return (src.r*gray_r_coeff +src.g*gray_g_coeff +src.b*gray_b_coeff);  
  8.     }  
  9.     //處理一行  
  10.     void colorToGrayLine_float(const Color32* src,Color32* dst,long width){  
  11.         for (long x = 0; x < width; ++x){  
  12.             int gray=(int)toGray_float(src[x]);  
  13.             dst[x]=Color32(gray,gray,gray,src[x].a);//R,G,B都設定為相同的亮度值,A不變  
  14.         }  
  15.     }  
  16. void colorToGray_float(const TPixels32Ref& src,const TPixels32Ref& dst){  
  17.     long width=std::min(src.width,dst.width);  
  18.     long height=std::min(src.height,dst.height);  
  19.     Color32* srcLine=src.pdata;  
  20.     Color32* dstLine=dst.pdata;  
  21.     for (long y = 0; y < height; ++y){  
  22.         colorToGrayLine_float(srcLine,dstLine,width);  
  23.         src.nextLine(srcLine);  
  24.         dst.nextLine(dstLine);  
  25.     }  
  26. }  

////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_float           145.49 FPS
////////////////////////////////////////////////////////////////////////////////

D: 將浮點運算轉化為定點數(整數)運算
    
  1. must_inline int toGray_int16(const Color32& src){  
  2.     const long bit=16;  
  3.     const int gray_r_coeff_int=(int)( gray_r_coeff*(1<<bit)+0.4999999 );  
  4.     const int gray_g_coeff_int=(int)( gray_g_coeff*(1<<bit)+0.4999999 );  
  5.     const int gray_b_coeff_int=(1<<bit)-gray_r_coeff_int-gray_g_coeff_int;  
  6.     return (src.r*gray_r_coeff_int +src.g*gray_g_coeff_int +src.b*gray_b_coeff_int) >> bit;  
  7. }  
  8. inline void colorToGrayLine_int16(const Color32* src,Color32* dst,long width){  
  9.     for (long x = 0; x < width; ++x){  
  10.         int gray=toGray_int16(src[x]);  
  11.         dst[x]=Color32(gray,gray,gray,src[x].a);  
  12.     }  
  13. }  
  14.  colorToGray_int16(const TPixels32Ref& src,const TPixels32Ref& dst){  
  15. long width=std::min(src.width,dst.width);  
  16. long height=std::min(src.height,dst.height);  
  17. Color32* srcLine=src.pdata;  
  18. Color32* dstLine=dst.pdata;  
  19. for (long y = 0; y < height; ++y){  
  20.     colorToGrayLine_int16(srcLine,dstLine,width);  
  21.     src.nextLine(srcLine);  
  22.     dst.nextLine(dstLine);  
  23. }  


////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_int16           355.33 FPS
////////////////////////////////////////////////////////////////////////////////

E: 做一個簡單的迴圈程式碼展開
  1. //四路展開  
  2. void colorToGrayLine_int16_expand4(const Color32* src,Color32* dst,long width){  
  3.     long widthFast=width>>2<<2;  
  4.     for (long x = 0; x < widthFast; x+=4){  
  5.         int gray0=toGray_int16(src[x  ]);  
  6.         int gray1=toGray_int16(src[x+1]);  
  7.         dst[x  ]=Color32(gray0,gray0,gray0,src[x  ].a);  
  8.         dst[x+1]=Color32(gray1,gray1,gray1,src[x+1].a);  
  9.         int gray2=toGray_int16(src[x+2]);  
  10.         int gray3=toGray_int16(src[x+3]);  
  11.         dst[x+2]=Color32(gray2,gray2,gray2,src[x+2].a);  
  12.         dst[x+3]=Color32(gray3,gray3,gray3,src[x+3].a);  
  13.     }  
  14.     //border  
  15.     if (width>widthFast)  
  16.         colorToGrayLine_int16(&src[widthFast],&dst[widthFast],width-widthFast);  
  17. }  
  18.  colorToGray_int16_expand4(const TPixels32Ref& src,const TPixels32Ref& dst){  
  19. long width=std::min(src.width,dst.width);  
  20. long height=std::min(src.height,dst.height);  
  21. Color32* srcLine=src.pdata;  
  22. Color32* dstLine=dst.pdata;  
  23. for (long y = 0; y < height; ++y){  
  24.     colorToGrayLine_int16_expand4(srcLine,dstLine,width);  
  25.     src.nextLine(srcLine);  
  26.     dst.nextLine(dstLine);  
  27. }  


////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_int16_expand4   413.22 FPS
////////////////////////////////////////////////////////////////////////////////

F: 一個特別的版本
   在高階語言範圍內進行單條指令多資料流計算,減少需要的乘法量;
在乘法運算代價比較高昂的cpu上應該效果不錯; (x86上速度可能慢)
  1. must_inline UInt32 toGray_int8_opMul(const Color32* src2Color){  
  2.     const UInt32 gray_r_coeff_8=(UInt32)( gray_r_coeff*(1<<8)+0.4999999);  
  3.     const UInt32 gray_g_coeff_8=(UInt32)( gray_g_coeff*(1<<8)+0.4999999);  
  4.     const UInt32 gray_b_coeff_8=(1<<8)-gray_r_coeff_8-gray_g_coeff_8;  
  5.     UInt32 RR,GG,BB;  
  6.     BB=src2Color[0].b | (src2Color[1].b<<16);  
  7.     GG=src2Color[0].g | (src2Color[1].g<<16);  
  8.     RR=src2Color[0].r | (src2Color[1].r<<16);  
  9.     BB*=gray_b_coeff_8;  
  10.     GG*=gray_g_coeff_8;  
  11.     RR*=gray_r_coeff_8;  
  12.     return BB+GG+RR;  
  13. }  
  14. void colorToGrayLine_int8_opMul(const Color32* src,Color32* dst,long width){  
  15.     long widthFast=width>>2<<2;  
  16.     for (long x = 0; x < widthFast; x+=4){  
  17.         UInt32 gray01=toGray_int8_opMul(&src[x  ]);  
  18.         int gray0=(gray01&0x0000FF00)>>8;  
  19.         int gray1=gray01>>24;  
  20.         dst[x  ]=Color32(gray0,gray0,gray0,src[x  ].a);  
  21.         dst[x+1]=Color32(gray1,gray1,gray1,src[x+1].a);  
  22.         UInt32 gray23=toGray_int8_opMul(&src[x+2]);  
  23.         int gray2=(gray23&0x0000FF00)>>8;  
  24.         int gray3=gray23>>24;  
  25.         dst[x+2]=Color32(gray2,gray2,gray2,src[x+2].a);  
  26.         dst[x+3]=Color32(gray3,gray3,gray3,src[x+3].a);  
  27.     }  
  28.     //border  
  29.     if (width>widthFast)  
  30.         colorToGrayLine_int16(&src[widthFast],&dst[widthFast],width-widthFast);  
  31. }  
  32.  colorToGray_int8_opMul(const TPixels32Ref& src,const TPixels32Ref& dst){  
  33. long width=std::min(src.width,dst.width);  
  34. long height=std::min(src.height,dst.height);  
  35. Color32* srcLine=src.pdata;  
  36. Color32* dstLine=dst.pdata;  
  37. for (long y = 0; y < height; ++y){  
  38.     colorToGrayLine_int8_opMul(srcLine,dstLine,width);  
  39.     src.nextLine(srcLine);  
  40.     dst.nextLine(dstLine);  
  41. }  

////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_int8_opMul      387.97 FPS
////////////////////////////////////////////////////////////////////////////////

G: 內聯彙編的MMX實現版本
   注意:這裡的MMX程式碼都只支援x86CPU(奔騰MMX以上CPU);
   在x64下不再有MMX暫存器,而應該使用SEE的XMM暫存器;
   而且在x64模式下vc2008編譯器還沒有提供內聯彙編的直接支援,而必須使用函式指令方式的實現;
   GCC編譯器也支援內聯彙編模式,但是彙編語法不同,請參考相應的說明;

  1.     void colorToGrayLine_MMX(const Color32* src,Color32* dst,long width){  
  2.         //const UInt32 gray_r_coeff_7=(UInt32)( gray_r_coeff*(1<<7)+0.4999999 );  
  3.         //const UInt32 gray_g_coeff_7=(UInt32)( gray_g_coeff*(1<<7)+0.4999999 );  
  4.         //const UInt32 gray_b_coeff_7=(1<<7)-gray_r_coeff_7-gray_g_coeff_7;  
  5.         // csMMX_rgb_coeff_w= short[ 0 , gray_r_coeff_7 , gray_g_coeff_7 , gray_b_coeff_7 ]  
  6.         const  UInt64   csMMX_rgb_coeff_w  = (((UInt64)0x00000026)<<32) | 0x004b000f;  
  7.         long widthFast=width>>1<<1;  
  8.         if (widthFast>0){  
  9.             asm{  
  10.                     pcmpeqb        mm5,mm5                // FF  FF  FF  FF  FF  FF  FF  FF  
  11.                     mov        ecx,widthFast  
  12.                     pxor        mm7,mm7                // 00  00  00  00  00  00  00  00  
  13.                     pcmpeqb        mm4,mm4                // FF  FF  FF  FF  FF  FF  FF  FF  
  14.                     mov     eax,src  
  15.                     mov     edx,dst  
  16.                     movq        mm6,csMMX_rgb_coeff_w  
  17.                     psrlw        mm5,15                //      1       1       1       1  
  18.                     lea     eax,[eax+ecx*4]  
  19.                     lea     edx,[edx+ecx*4]      
  20.                     pslld        mm4,24                // FF  00  00  00  FF  00  00  00  
  21.                     neg     ecx  
  22.                      
  23.                   loop_beign:  
  24.                     movq        mm0,[eax+ecx*4]        // A1  R1  G1  B1  A0  R0  G0  B0  
  25.                     movq        mm1,mm0  
  26.                     movq        mm3,mm0  
  27.                     punpcklbw   mm0,mm7                // 00  A0  00  R0  00  G0  00  B0  
  28.                     punpckhbw   mm1,mm7                // 00  A1  00  R1  00  G1  00  B1  
  29.                     pmaddwd     mm0,mm6             // R0*r_coeff      G0*g_coeff+B0*b_coeff  
  30.                     pmaddwd     mm1,mm6             // R1*r_coeff      G1*g_coeff+B1*b_coeff  
  31.                     pand        mm3,mm4             // A1  00  00  00  A0  00  00  00  
  32.                     packssdw    mm0,mm1             // sR1     sG1+sB1 sR0     sG0+sB0  
  33.                     pmaddwd     mm0,mm5             // sR1+sG1+sB1     sR0+sG0+sB0  
  34.                     psrld       mm0,7               // 00 00 00 Gray1  00 00 00 Gray0  
  35.                     movq        mm1,mm0  
  36.                     movq        mm2,mm0  
  37.                     pslld        mm1,8                // 00 00 Gray1 00  00 00 Gray0 00  
  38.                     por         mm0,mm3  
  39.                     pslld        mm2,16                // 00 Gray1 00 00  00 Gray0 00 00  
  40.                     por         mm0,mm1  
  41.                     por         mm0,mm2             // A1 Gray1 Gray1 Gray1  A0 Gray0 Gray0 Gray0  
  42.                     movq [edx+ecx*4],mm0  
  43.                     add     ecx,2                   
  44.                     jnz     loop_beign  
  45.             }  
  46.         }  
  47.         //border  
  48.         if (width>widthFast)  
  49.             colorToGrayLine_int16(&src[widthFast],&dst[widthFast],width-widthFast);  
  50.     }  
  51. void colorToGray_MMX(const TPixels32Ref& src,const TPixels32Ref& dst){  
  52.     long width=std::min(src.width,dst.width);  
  53.     long height=std::min(src.height,dst.height);  
  54.     Color32* srcLine=src.pdata;  
  55.     Color32* dstLine=dst.pdata;  
  56.     for (long y = 0; y < height; ++y){  
  57.         colorToGrayLine_MMX(srcLine,dstLine,width);  
  58.         src.nextLine(srcLine);  
  59.         dst.nextLine(dstLine);  
  60.     }  
  61.     asm{  
  62.         emms //MMX使用結束  
  63.     }  
  64. }  


////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_MMX             590.84 FPS
////////////////////////////////////////////////////////////////////////////////

H: 優化寫緩衝的內聯彙編的MMX實現版本
  該版本相應於上面的MMX版本只改寫了兩句:
   一是寫記憶體的movq [edx+ecx*4],mm0 改成了 movntq [edx+ecx*4],mm0 繞過快取
   二是函式結束的時候呼叫sfence重新整理寫入
  完整程式碼如下:

  1. void colorToGrayLine_MMX2(const Color32* src,Color32* dst,long width){  
  2.     //const UInt32 gray_r_coeff_7=(UInt32)( gray_r_coeff*(1<<7)+0.4999999 );  
  3.     //const UInt32 gray_g_coeff_7=(UInt32)( gray_g_coeff*(1<<7)+0.4999999 );  
  4.     //const UInt32 gray_b_coeff_7=(1<<7)-gray_r_coeff_7-gray_g_coeff_7;  
  5.     // csMMX_rgb_coeff_w= short[ 0 , gray_r_coeff_7 , gray_g_coeff_7 , gray_b_coeff_7 ]  
  6.     const  UInt64   csMMX_rgb_coeff_w  = (((UInt64)0x00000026)<<32) | 0x004b000f;  
  7.     long widthFast=width>>1<<1;  
  8.     if (widthFast>0){  
  9.         asm{  
  10.                 pcmpeqb        mm5,mm5                // FF  FF  FF  FF  FF  FF  FF  FF  
  11.                 mov        ecx,widthFast  
  12.                 pxor        mm7,mm7                // 00  00  00  00  00  00  00  00  
  13.                 pcmpeqb        mm4,mm4                // FF  FF  FF  FF  FF  FF  FF  FF  
  14.                 mov     eax,src  
  15.                 mov     edx,dst  
  16.                 movq        mm6,csMMX_rgb_coeff_w  
  17.                 psrlw        mm5,15                //      1       1       1       1  
  18.                 lea     eax,[eax+ecx*4]  
  19.                 lea     edx,[edx+ecx*4]      
  20.                 pslld        mm4,24                // FF  00  00  00  FF  00  00  00  
  21.                 neg     ecx  
  22.                  
  23.               loop_beign:  
  24.                 movq        mm0,[eax+ecx*4]        // A1  R1  G1  B1  A0  R0  G0  B0  
  25.                 movq        mm1,mm0  
  26.                 movq        mm3,mm0  
  27.                 punpcklbw   mm0,mm7                // 00  A0  00  R0  00  G0  00  B0  
  28.                 punpckhbw   mm1,mm7                // 00  A1  00  R1  00  G1  00  B1  
  29.                 pmaddwd     mm0,mm6             // R0*r_coeff      G0*g_coeff+B0*b_coeff  
  30.                 pmaddwd     mm1,mm6             // R1*r_coeff      G1*g_coeff+B1*b_coeff  
  31.                 pand        mm3,mm4             // A1  00  00  00  A0  00  00  00  
  32.                 packssdw    mm0,mm1             // sR1     sG1+sB1 sR0     sG0+sB0  
  33.                 pmaddwd     mm0,mm5             // sR1+sG1+sB1     sR0+sG0+sB0  
  34.                 psrld       mm0,7               // 00 00 00 Gray1  00 00 00 Gray0  
  35.                 movq        mm1,mm0  
  36.                 movq        mm2,mm0  
  37.                 pslld        mm1,8                // 00 00 Gray1 00  00 00 Gray0 00  
  38.                 por         mm0,mm3  
  39.                 pslld        mm2,16                // 00 Gray1 00 00  00 Gray0 00 00  
  40.                 por         mm0,mm1  
  41.                 por         mm0,mm2             // A1 Gray1 Gray1 Gray1  A0 Gray0 Gray0 Gray0  
  42.                 movntq [edx+ecx*4],mm0  //和colorToGrayLine_MMX的不同之處  
  43.                 add     ecx,2                   
  44.                 jnz     loop_beign  
  45.         }  
  46.     }  
  47.     //border  
  48.     if (width>widthFast)  
  49.         colorToGrayLine_int16(&src[widthFast],&dst[widthFast],width-widthFast);  
  50. }  
  51.  colorToGray_MMX2(const TPixels32Ref& src,const TPixels32Ref& dst){  
  52. long width=std::min(src.width,dst.width);  
  53. long height=std::min(src.height,dst.height);  
  54. Color32* srcLine=src.pdata;  
  55. Color32* dstLine=dst.pdata;  
  56. for (long y = 0; y < height; ++y){  
  57.     colorToGrayLine_MMX2(srcLine,dstLine,width);  
  58.     src.nextLine(srcLine);  
  59.     dst.nextLine(dstLine);  
  60. }  
  61. asm{  
  62.     sfence  //重新整理寫入  
  63.     emms  
  64. }  



////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_MMX2            679.50 FPS
////////////////////////////////////////////////////////////////////////////////

I: 使用MMX函式指令方式的實現
  MMX/SSE等特殊指令除了內聯彙編來使用外,也可以使用函式指令方式的實現,從而在多種
編譯器下都可以使用SIMD相關指令,可移植性也會好很多;
  但現在看來,vc對此的優化還不夠,還可能遇到編譯器的實現bug;
  (可以考慮使用intel的編譯器編譯這些程式碼,感覺優化能力很不錯)

  1. #include <mmintrin.h>   //mmx  
  2. //#include <mm3dnow.h>    //3dnow  
  3. #include <xmmintrin.h>  //sse  
  4. //#include <emmintrin.h>  //sse2  
  5. //#include <pmmintrin.h>  //sse3  
  6. //#include <tmmintrin.h>  //ssse3  
  7. //#include <intrin.h>     //sse4a  
  8. //#include <smmintrin.h>  //sse4.1  
  9. //#include <nmmintrin.h>  //sse4.2  
  10. //----------------------------------  
  11.     void colorToGrayLine_MMX_mmh(const Color32* src,Color32* dst,long width){  
  12.         //const UInt32 gray_r_coeff_7=(UInt32)( gray_r_coeff*(1<<7)+0.4999999 );  
  13.         //const UInt32 gray_g_coeff_7=(UInt32)( gray_g_coeff*(1<<7)+0.4999999 );  
  14.         //const UInt32 gray_b_coeff_7=(1<<7)-gray_r_coeff_7-gray_g_coeff_7;  
  15.         // csMMX_rgb_coeff_w= short[ 0 , gray_r_coeff_7 , gray_g_coeff_7 , gray_b_coeff_7 ]  
  16.         long widthFast=width>>1<<1;  
  17.         if (widthFast>0){  
  18.             const UInt64 csMMX_rgb_coeff_w  =(((UInt64)0x00000026)<<32) | 0x004b000f;  
  19.             const __m64 mm6=*(const __m64*)&csMMX_rgb_coeff_w;  
  20.             const __m64 mm7=_mm_setzero_si64();     //mm?變數值同colorToGrayLine_MMX中的mmx值一致  
  21.             __m64 mm5=_mm_cmpeq_pi8(mm7,mm7);       //想寫成__m64 mm5; mm5=_mm_cmpeq_pi8(mm5,mm5);但會出錯:(  
  22.             const __m64 mm4=_mm_slli_pi32(mm5,24);  // ...  
  23.             mm5=_mm_srli_pi16(mm5,15);              // ...  
  24.             for (long x = 0; x < widthFast; x+=2){  
  25.                 __m64 mm0=*(__m64*)&src[x];  
  26.                 __m64 mm1=mm0;  
  27.                 __m64 mm3=mm0;  
  28.                 mm0=_mm_unpacklo_pi8(mm0,mm7);  
  29.                 mm1=_mm_unpackhi_pi8(mm1,mm7);  
  30.                 mm0=_mm_madd_pi16(mm0,mm6);  
  31.                 mm1=_mm_madd_pi16(mm1,mm6);  
  32.                 mm3=_mm_and_si64(mm3,mm4);  
  33.                 mm0=_mm_packs_pi32(mm0,mm1);  
  34.                 mm0=_mm_madd_pi16(mm0,mm5);  
  35.                 mm0=_mm_srli_pi32(mm0,7);  
  36.                 mm1=mm0;  
  37.                 __m64 mm2=mm0;  
  38.                 mm1=_mm_slli_pi32(mm1,8);  
  39.                 mm0=_mm_or_si64(mm0,mm3);  
  40.                 mm2=_mm_slli_pi32(mm2,16);  
  41.                 mm0=_mm_or_si64(mm0,mm1);  
  42.                 mm0=_mm_or_si64(mm0,mm2);  
  43.                 *(__m64*)&dst[x]=mm0;  
  44.             }  
  45.         }  
  46.         //border  
  47.         if (width>widthFast)  
  48.             colorToGrayLine_int16(&src[widthFast],&dst[widthFast],width-widthFast);  
  49.     }  
  50. void colorToGray_MMX_mmh(const TPixels32Ref& src,const TPixels32Ref& dst){  
  51.     long width=std::min(src.width,dst.width);  
  52.     long height=std::min(src.height,dst.height);  
  53.     Color32* srcLine=src.pdata;  
  54.     Color32* dstLine=dst.pdata;  
  55.     for (long y = 0; y < height; ++y){  
  56.         colorToGrayLine_MMX_mmh(srcLine,dstLine,width);  
  57.         src.nextLine(srcLine);  
  58.         dst.nextLine(dstLine);  
  59.     }  
  60.     _mm_empty(); //MMX使用結束  
  61. }  


////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_MMX_mmh         508.69 FPS
////////////////////////////////////////////////////////////////////////////////


 優化寫緩衝的使用MMX函式指令方式的實現

  1.     void colorToGrayLine_MMX2_mmh(const Color32* src,Color32* dst,long width){  
  2.         //const UInt32 gray_r_coeff_7=(UInt32)( gray_r_coeff*(1<<7)+0.4999999 );  
  3.         //const UInt32 gray_g_coeff_7=(UInt32)( gray_g_coeff*(1<<7)+0.4999999 );  
  4.         //const UInt32 gray_b_coeff_7=(1<<7)-gray_r_coeff_7-gray_g_coeff_7;  
  5.         // csMMX_rgb_coeff_w= short[ 0 , gray_r_coeff_7 , gray_g_coeff_7 , gray_b_coeff_7 ]  
  6.         long widthFast=width>>1<<1;  
  7.         if (widthFast>0){  
  8.             const UInt64 csMMX_rgb_coeff_w  =(((UInt64)0x00000026)<<32) | 0x004b000f;  
  9.             const __m64 mm6=*(const __m64*)&csMMX_rgb_coeff_w;  
  10.             const __m64 mm7=_mm_setzero_si64();     //mm?變數值同colorToGrayLine_MMX中的mmx值一致  
  11.             __m64 mm5=_mm_cmpeq_pi8(mm7,mm7);       // ...  
  12.             const __m64 mm4=_mm_slli_pi32(mm5,24);  // ...  
  13.             mm5=_mm_srli_pi16(mm5,15);              // ...  
  14.             for (long x = 0; x < widthFast; x+=2){  
  15.                 __m64 mm0=*(__m64*)&src[x];  
  16.                 __m64 mm1=mm0;  
  17.                 __m64 mm3=mm0;  
  18.                 mm0=_mm_unpacklo_pi8(mm0,mm7);  
  19.                 mm1=_mm_unpackhi_pi8(mm1,mm7);  
  20.                 mm0=_mm_madd_pi16(mm0,mm6);  
  21.                 mm1=_mm_madd_pi16(mm1,mm6);  
  22.                 mm3=_mm_and_si64(mm3,mm4);  
  23.                 mm0=_mm_packs_pi32(mm0,mm1);  
  24.                 mm0=_mm_madd_pi16(mm0,mm5);  
  25.                 mm0=_mm_srli_pi32(mm0,7);  
  26.                 mm1=mm0;  
  27.                 __m64 mm2=mm0;  
  28.                 mm1=_mm_slli_pi32(mm1,8);  
  29.                 mm0=_mm_or_si64(mm0,mm3);  
  30.                 mm2=_mm_slli_pi32(mm2,16);  
  31.                 mm0=_mm_or_si64(mm0,mm1);  
  32.                 mm0=_mm_or_si64(mm0,mm2);  
  33.                 //*(__m64*)&dst[x]=mm0;  
  34.                 _mm_stream_pi((__m64*)&dst[x],mm0);  
  35.             }  
  36.         }  
  37.         //border  
  38.         if (width>widthFast)  
  39.             colorToGrayLine_int16(&src[widthFast],&dst[widthFast],width-widthFast);  
  40.     }  
  41. void colorToGray_MMX2_mmh(const TPixels32Ref& src,const TPixels32Ref& dst){  
  42.     long width=std::min(src.width,dst.width);  
  43.     long height=std::min(src.height,dst.height);  
  44.     Color32* srcLine=src.pdata;  
  45.     Color32* dstLine=dst.pdata;  
  46.     for (long y = 0; y < height; ++y){  
  47.         colorToGrayLine_MMX2_mmh(srcLine,dstLine,width);  
  48.         src.nextLine(srcLine);  
  49.         dst.nextLine(dstLine);  
  50.     }  
  51.     _mm_sfence();//重新整理寫入  
  52.     _mm_empty(); //MMX使用結束  
  53. }  


////////////////////////////////////////////////////////////////////////////////
//速度測試
//==============================================================================
// colorToGray_MMX2_mmh        540.78 FPS
////////////////////////////////////////////////////////////////////////////////

J:把測試成績放在一起:

////////////////////////////////////////////////////////////////////////////////
//CPU: AMD64x2 4200+(2.33G)  800*600 to 800*600
//==============================================================================
// colorToGray_float           145.49 FPS
// colorToGray_int16           355.33 FPS
// colorToGray_int16_expand4   413.22 FPS
// colorToGray_int8_opMul      387.97 FPS
// colorToGray_MMX             590.84 FPS
// colorToGray_MMX2            679.50 FPS
// colorToGray_MMX_mmh         508.69 FPS
// colorToGray_MMX2_mmh        540.78 FPS
////////////////////////////////////////////////////////////////////////////////

ps:用SSE的浮點指令的版本/用SSE2整數指令的版本/利用SSE3的水平加指令等的實現版本有機會時再補充
ps:SIMD特殊指令集的使用框架請參見我的<YUV視訊格式到RGB32格式轉換的速度優化 中篇>一文,從而
根據CPU對指令集的支援情況動態的呼叫最優的實現函式版本;


相關文章