SIMD(MMX/SSE/AVX)變數命名規範心得
當使用Intrinsics函式來操作SIMD指令集(MMX/SSE/AVX等)時,會面對不同長度的SIMD資料型別,其中又分為多種緊縮格式。為此,我設計了一套SIMD變數命名規範,可以有效的提高程式碼的可讀性。
一、SIMD資料型別簡介
SIMD資料型別有——
__m64:64位緊縮整數(MMX)。
__m128:128位緊縮單精度(SSE)。
__m128d:128位緊縮雙精度(SSE2)。
__m128i:128位緊縮整數(SSE2)。
__m256:256位緊縮單精度(AVX)。
__m256d:256位緊縮雙精度(AVX)。
__m256i:256位緊縮整數(AVX)。
注:緊縮整數包括了8位、16位、32位、64位的帶符號和無符號整數。
這些資料型別與暫存器的對應關係為——
64位MM暫存器(MM0~MM7):__m64。
128位SSE暫存器(XMM0~XMM15):__m128、__m128d、__m128i。
256位AVX暫存器(YMM0~YMM15):__m256、__m256d、__m256i。
二、SIMD變數命名規範
參考匈牙利命名法(Hungarian notation),在變數名前面增加型別字首。
型別字首為3個小寫字母,首字母代表暫存器寬度,最後兩個字母代表緊縮資料型別。
暫存器寬度(首字母)——
m:64位MM暫存器。對應 __m64
x:128位SSE暫存器。對應 __m128、__m128d、__m128i。
y:256位AVX暫存器。對應 __m256、__m256d、__m256i。
緊縮資料型別(兩個字母)——
mb:8位資料。用於只知道長度、不知道具體緊縮格式時。(b:Byte)
mw:16位資料。(w:Word)
md:32位資料。(d:DoubleWord)
mq:64位資料。(q:QuadWord)
mo:128位資料。(o:OctaWord)
mh:256位資料。(h:HexWord)
ub:8位無符號整數。
uw:16位無符號整數。
ud:32位無符號整數。
uq:64位無符號整數。
ib:8位帶符號整數。
iw:16位帶符號整數。
id:32位帶符號整數。
iq:64位帶符號整數。
fh:16位浮點數,即半精度浮點數。(h:Half)
fs:32位浮點數,即單精度浮點數。(s:Single)
fd:64位浮點數,即雙精度浮點數。(d:double)
例如——
mub:64位緊縮位元組(64位MMX暫存器,其中存放了8個8位無符號整數)。
xfs:128位緊縮單精度(128位SSE暫存器,其中存放了4個單精度浮點數)。
xid:128位緊縮帶符號字(128位SSE暫存器,其中存放了4個32位帶符號整數)。
yfd:256位緊縮雙精度(256位AVX暫存器,其中存放了4個雙精度浮點數)。
yfh:256位緊縮半精度(256位AVX暫存器,其中存放了16個半精度浮點數)。
三、示例程式碼
例如SSE累加求和程式——
int sum3_Intrinsics(int *a, int size) { if (NULL==a) return 0; if (size<0) return 0; int s = 0; // 返回值 __m128i xidSum = _mm_setzero_si128(); // 累積。[SSE2] 賦初值0 __m128i xidLoad; // 載入 int cntBlock = size / 4; // 塊數。SSE暫存器能一次處理4個DWORD int cntRem = size & 3; // 剩餘數量 __m128i* p = (__m128i*)a; for(int i = 0; i < cntBlock; ++i) { xidLoad = _mm_load_si128(p); // [SSE2] 載入 xidSum = _mm_add_epi32(xidSum, xidLoad); // [SSE2] 帶符號32位緊縮加法 ++p; } // 處理剩下的 int* q = (int*)p; for(int i = 0; i < cntRem; ++i) s += q[i]; // 將累加值合併 xidSum = _mm_hadd_epi32(xidSum, xidSum); // [SSSE3] 帶符號32位水平加法 xidSum = _mm_hadd_epi32(xidSum, xidSum); s += _mm_cvtsi128_si32(xidSum); // [SSE2] 返回低32位 return s; }