矩陣轉置的SSE彙編優化藝術以及ARM cortext 彙編優化

轉至http://blog.csdn.net/feixiang_john/article/details/8438658

平時我們做圖像處理或者視頻處理, 很多地方會用到矩陣轉置:

比如: DCT變換, 圖像旋轉, 圖像濾波, 以及一些數據的內存行和列的交換等, 會大量使用轉置這個動作.

然而由於數據量很大,處理速度很慢!如何來提高處理速度呢?

下面看看分析: 

HEVC中有個地方是如下這樣實現(直接行和列對應的位置交換):

  1. Pel  tmp;  
  2. for (k=0;k<blkSize-1;k++)  
  3. {  
  4.   for (l=k+1;l<blkSize;l++)  
  5.   {  
  6.     tmp                 = pDst[k*dstStride+l];  
  7.     pDst[k*dstStride+l] = pDst[l*dstStride+k];  
  8.     pDst[l*dstStride+k] = tmp;  
  9.   }  
  10. }  


 

如何用匯編來實現呢?

我們先用SSE彙編來實現一個8X8的矩陣轉置吧: 這裏輸入地址pSrc_128[i] 和輸出地址pDst_128[i]可以相同也可以不同:

相同的話就是原地轉置, 不同的話就是非原地轉置.

  1.   __m128i* m_pSrc_tmp = pSrc_128[i];  
  2.   __m128i* m_pDst_tmp = pDst_128[i];  
  3.   __m128i Org_8_0,Org_8_1, Org_8_2, Org_8_3;  
  4.   __m128i tttt1,tttt2,tttt3,tttt4,tttt33,tttt44;  
  5.   __m128i tttt5,tttt6, tttt7, tttt8;  
  6.   int stride_ii = dstStride>>3;  
  7. //one  
  8.   Org_8_0 = _mm_load_si128(m_pSrc_tmp);  
  9.   m_pSrc_tmp+=8;  
  10.   Org_8_1 = _mm_load_si128(m_pSrc_tmp);  
  11.   m_pSrc_tmp+=8;  
  12.   Org_8_2 = _mm_load_si128(m_pSrc_tmp);  
  13.   m_pSrc_tmp+=8;  
  14.   Org_8_3 = _mm_load_si128(m_pSrc_tmp);  
  15.   m_pSrc_tmp+=8;  
  16.   
  17.   tttt1 = _mm_unpacklo_epi16(Org_8_0, Org_8_1);  
  18.   tttt2 = _mm_unpacklo_epi16(Org_8_2, Org_8_3);  
  19.   tttt3 = _mm_unpackhi_epi16(Org_8_0, Org_8_1);  
  20.   tttt4 = _mm_unpackhi_epi16(Org_8_2, Org_8_3);  
  21.   
  22.   tttt5 = _mm_unpacklo_epi32(tttt1, tttt2);  
  23.   tttt6 = _mm_unpackhi_epi32(tttt1, tttt2);  
  24.   
  25.   Org_8_0 = _mm_load_si128(m_pSrc_tmp);  
  26.   m_pSrc_tmp+=8;;   
  27.   Org_8_1 = _mm_load_si128(m_pSrc_tmp);  
  28.   m_pSrc_tmp+=8;  
  29.   Org_8_2 = _mm_load_si128(m_pSrc_tmp);  
  30.   m_pSrc_tmp+=8;   
  31.   Org_8_3 = _mm_load_si128(m_pSrc_tmp);  
  32.   //m_pSrc_tmp+=8;  
  33.   
  34.   tttt1 = _mm_unpacklo_epi16(Org_8_0, Org_8_1);  
  35.   tttt2 = _mm_unpacklo_epi16(Org_8_2, Org_8_3);  
  36.   tttt33 = _mm_unpackhi_epi16(Org_8_0, Org_8_1);  
  37.   tttt44 = _mm_unpackhi_epi16(Org_8_2, Org_8_3);  
  38.   
  39.   tttt7 = _mm_unpacklo_epi32(tttt1, tttt2);  
  40.   tttt8 = _mm_unpackhi_epi32(tttt1, tttt2);  
  41.   
  42.   tttt1 = _mm_unpacklo_epi64(tttt5, tttt7);  
  43.   tttt2 = _mm_unpackhi_epi64(tttt5, tttt7);  
  44.   _mm_storeu_si128(m_pDst_tmp, tttt1);  
  45.   m_pDst_tmp+=stride_ii;  
  46.   _mm_storeu_si128(m_pDst_tmp, tttt2);  
  47.   m_pDst_tmp+=stride_ii;  
  48.   tttt5 = _mm_unpacklo_epi64(tttt6, tttt8);  
  49.   tttt7 = _mm_unpackhi_epi64(tttt6, tttt8);  
  50.   _mm_storeu_si128(m_pDst_tmp, tttt5);  
  51.   m_pDst_tmp+=stride_ii;  
  52.   _mm_storeu_si128(m_pDst_tmp, tttt7);  
  53.   m_pDst_tmp+=stride_ii;  
  54.   
  55. //tow  
  56.   tttt5 = _mm_unpacklo_epi32(tttt3, tttt4);  
  57.   tttt6 = _mm_unpackhi_epi32(tttt3, tttt4);  
  58.   
  59.   tttt7 = _mm_unpacklo_epi32(tttt33, tttt44);  
  60.   tttt8 = _mm_unpackhi_epi32(tttt33, tttt44);  
  61.   
  62.   tttt1 = _mm_unpacklo_epi64(tttt5, tttt7);  
  63.   tttt2 = _mm_unpackhi_epi64(tttt5, tttt7);  
  64.   _mm_storeu_si128(m_pDst_tmp, tttt1);  
  65.   m_pDst_tmp+=stride_ii;  
  66.   _mm_storeu_si128(m_pDst_tmp, tttt2);  
  67.   m_pDst_tmp+=stride_ii;  
  68.   tttt5 = _mm_unpacklo_epi64(tttt6, tttt8);  
  69.   tttt7 = _mm_unpackhi_epi64(tttt6, tttt8);  
  70.   _mm_storeu_si128(m_pDst_tmp, tttt5);  
  71.   m_pDst_tmp+=stride_ii;  
  72.   _mm_storeu_si128(m_pDst_tmp, tttt7);  


要實現的是NXN的轉置,如何實現呢:

基於8X8來實現NXN的塊或者圖像的轉置:

這裏先把NXN劃分爲size_case 個8X8, 然後循環調用8X8的轉置!

 

  1.  __m128i* pDst_128[64];  
  2.  __m128i* pSrc_128[64];  
  3.  int size_case = (blkSize>>3);  
  4.  dstStride = dstStride_tmp;  
  5.  for(int y = 0; y<size_case; y++)//對所有8x8的塊進行地址映射  
  6.   for(int x = 0; x<size_case; x++)  
  7.   {  
  8.    pSrc_128[y*size_case + x] = (__m128i*)(pDst + 8*x + y*8*64);  
  9.    pDst_128[y*size_case + x] = (__m128i*)(rpDst + 8*y + x*8*dstStride);  
  10.   }  
  11.   
  12.  size_case = size_case*size_case;  
  13.  for(int  i = 0;i <size_case; i++)//開始轉置  
  14.  {  
  15.   
  16.     8x8轉置的代碼:  
  17.   
  18. }  

通過比較, 用SSE彙編優化實現轉置比用純 C代碼實現的轉置速度快5倍左右! 

 

同樣在ARM cortext上的彙編優化也是基於這個原理:

主要循環體代碼如下:

  1. VTRN.16 q8, q9  
  2. VTRN.16 q10, q11  
  3. VTRN.16 q4, q5  
  4. VTRN.16 q6, q7  
  5. VTRN.32 q8, q10  
  6. VTRN.32 q9, q11  
  7. VTRN.32 q4, q6  
  8. VTRN.32 q5, q7  
  9. VSWP d17, d8  
  10. VSWP d19, d10  
  11. VSWP d21, d12  
  12. VSWP d23, d14     


感興趣的可以自己調試下!

 

當然DSP上也是同樣的方法, 只是涉及到的指令不同而已!

 

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