SSE是很常見的一個X86平台的指令集,早在P4時代就已經出現了。後來INTEL又接連著推出了SSE2,SSE3,SSE4等(不過可沒有SSE5,原本規劃是有的,後來INTEL獨立發展了新一代AVX指令集旨在取代SSE,關於AVX現在資料還不是很多,用的也沒有SSE普遍。畢竟支持AVX的CPU也不多,像我的T4400就不支持)。
廢話不多說,還是來點實在的東西。大家都知道浮點數運算比起整數運算,速度的確是非常緩慢,很多領域比如圖像處理中,需要大量用到浮點數運算,此時CPU就是一個很顯著的瓶頸,為了提高浮點數性能,我們有兩個方法:
1,化浮點為整形:即盡量通過某種數學變換將原來的浮點數運算變成整數運算。
2,使用SSE這類指令集:顯然這種方法是本文重點,不過方法1也會一並用起。
以一個很常見的圖像彩色轉灰度為例。
根據色彩學上的一些理論,將一個RGB彩色像素轉換成灰度,實際上是一個1*3矩陣和一個3*1矩陣相乘,說白了就是如下過程:
設原像素為p0 = (r0,g0,b0),轉換為s=(r0*0.3,g0*0.6,b0*0.1),然後新的灰度像素p1=(s,s,s)。
這裡可以看到,求得s值這一步中,有三次浮點運算,我們可以用方法1將這裡的浮點運算暫時化為整數(全部乘以10),即
s=(r0*3,g0*6,b0*1),最後一次性除以10。
具體代碼如下:
void doProcess(PBYTE pIn, DWORD size, DWORD width, DWORD height, DWORD bitCount)
{
DWORD dwRGBSum = 0;
for(DWORD dwIndex = 0; dwIndex < size; dwIndex+=3)
{
dwRGBSum =
1 * pIn[dwIndex+0] + //Blue
6 * pIn[dwIndex+1] + //Green
3 * pIn[dwIndex+2]; //Red
dwRGBSum /= 10.0;
pIn[dwIndex+0] = dwRGBSum;
pIn[dwIndex+1] = dwRGBSum;
pIn[dwIndex+2] = dwRGBSum;
}
}
現在我們再來使用SSE來進一步優化。
SSE一次性可以處理128位的運算,即4個浮點數。因而我們將四次除法放在一次進行,核心的一個數據結構是__m128,這是一個聯合體,具體參見其源碼。
SSE中批量浮點數乘法對應的C函數是_mm_mul_ps。用法可以參考MSDN或者INTEL官方網站上的一個PDF。
void doProcess(PBYTE pIn, DWORD size, DWORD width, DWORD height, DWORD bitCount)
{
UINT16 dwRGBSum0 = 0;
UINT16 dwRGBSum1 = 0;
UINT16 dwRGBSum2 = 0;
UINT16 dwRGBSum3 = 0;
for(DWORD idx = 0; idx < size; idx+=12)
{
dwRGBSum0 =
1 * pIn[idx+0] + //Blue
6 * pIn[idx+1] + //Green
3 * pIn[idx+2]; //Red
dwRGBSum1 =
1 * pIn[idx+3] + //Blue
6 * pIn[idx+4] + //Green
3 * pIn[idx+5]; //Red
dwRGBSum2 =
1 * pIn[idx+6] + //Blue
6 * pIn[idx+7] + //Green
3 * pIn[idx+8]; //Red
dwRGBSum3 =
1 * pIn[idx+9] + //Blue
6 * pIn[idx+10] + //Green
3 * pIn[idx+11]; //Red
__m128 old = _mm_set_ps(dwRGBSum0, dwRGBSum1, dwRGBSum2, dwRGBSum3);
__m128 ret = _mm_mul_ps(old, vec);
pIn[idx+0] = pIn[idx+1] = pIn[idx+2] = (BYTE)ret.m128_f32[3];
pIn[idx+3] = pIn[idx+4] = pIn[idx+5] = (BYTE)ret.m128_f32[2];
pIn[idx+6] = pIn[idx+7] = pIn[idx+8] = (BYTE)ret.m128_f32[1];
pIn[idx+9] = pIn[idx+10] = pIn[idx+11] = (BYTE)ret.m128_f32[0];
}
}
代碼看上去要比原來的復雜許多,不過其實原理很簡單的,原來一次性處理一個像素,現在一次性處理4個,性能和效率大大提升了。
這個代碼其實還可以優化的,因為SSE內除了浮點可以批量處理,整數也是可以的,對應的數據結構是__m128i。涉及到一些矩陣的知識,我就不多說了。
上面這個優化性能測試結果還是很明顯的,原來的程序對一個2560*1600,24位色深的圖片進行轉換,需要將近800ms的時間,優化後,只需450ms了,提高了將近一倍。
本文出自 “Kevx's Blog” 博客