[英]int vs short vectorization
我有以下內核向量化的整數數組:
long valor = 0, i=0;
__m128i vsum, vecPi, vecCi, vecQCi;
vsum = _mm_set1_epi32(0);
int32_t * const pA = A->data;
int32_t * const pB = B->data;
int sumDot[1];
for( ; i<SIZE-3 ;i+=4){
vecPi = _mm_loadu_si128((__m128i *)&(pA)[i] );
vecCi = _mm_loadu_si128((__m128i *)&(pB)[i] );
vecQCi = _mm_mullo_epi32(vecPi,vecCi);
vsum = _mm_add_epi32(vsum,vecQCi);
}
vsum = _mm_hadd_epi32(vsum, vsum);
vsum = _mm_hadd_epi32(vsum, vsum);
_mm_storeu_si128((__m128i *)&(sumDot), vsum);
for( ; i<SIZE; i++)
valor += A->data[i] * B->data[i];
valor += sumDot[0];
它工作正常。 但是,如果我將 A 和 B 的數據類型更改為short而不是int ,我不應該使用以下代碼:
long valor = 0, i=0;
__m128i vsum, vecPi, vecCi, vecQCi;
vsum = _mm_set1_epi16(0);
int16_t * const pA = A->data;
int16_t * const pB = B->data;
int sumDot[1];
for( ; i<SIZE-7 ;i+=8){
vecPi = _mm_loadu_si128((__m128i *)&(pA)[i] );
vecCi = _mm_loadu_si128((__m128i *)&(pB)[i] );
vecQCi = _mm_mullo_epi16(vecPi,vecCi);
vsum = _mm_add_epi16(vsum,vecQCi);
}
vsum = _mm_hadd_epi16(vsum, vsum);
vsum = _mm_hadd_epi16(vsum, vsum);
_mm_storeu_si128((__m128i *)&(sumDot), vsum);
for( ; i<SIZE; i++)
valor += A->data[i] * B->data[i];
valor += sumDot[0];
這第二個內核不起作用,我不知道為什么。 我知道第一種和第二種情況下向量的所有條目都是相同的(也沒有溢出)。 有人可以幫我找出錯誤嗎?
謝謝
這是我看到的一些事情。
在int
和short
情況下,當您將__m128
存儲到sumDot
,您在遠小於128 位的目標上使用_mm_storeu_si128
。 這意味着你一直在破壞記憶,幸運的是你沒有被咬。
sumDot
是int[1]
即使在short
情況下,您將兩個short
存儲在一個int
,然后將其作為int
讀取。 在short
情況下,您缺少一個水平向量減少步驟。 請記住,既然每個向量有 8 個short
s,您現在必須有 log_2(8) = 3 個向量縮減步驟。
vsum = _mm_hadd_epi16(vsum, vsum); vsum = _mm_hadd_epi16(vsum, vsum); vsum = _mm_hadd_epi16(vsum, vsum);
(可選)由於您已經使用 SSE4.1,不妨使用它的優點之一: PEXTR*
指令。 他們獲取要從中提取的車道的索引。 您對底部車道(車道 0)感興趣,因為這是矢量減少后總和結束的地方。
/* 32-bit */ sumDot[0] = _mm_extract_epi32(vsum, 0); /* 16-bit */ sumDot[0] = _mm_extract_epi16(vsum, 0);
編輯:顯然編譯器不會對使用_mm_extract_epi16
提取的 16 位字進行符號擴展。 你必須說服它自己這樣做。
/* 32-bit */ sumDot[0] = (int32_t)_mm_extract_epi32(vsum, 0); /* 16-bit */ sumDot[0] = (int16_t)_mm_extract_epi16(vsum, 0);
EDIT2 :我找到了一個更好的解決方案! 它使用完全相同的指令,我們需要( PMADDWD
),並且是相同的,只是迭代范圍是不同的32位代碼,而不是_mm_mullo_epi16
使用_mm_madd_epi16
在循環。 這僅需要兩個 32 位向量縮減階段。 http://pastebin.com/A9ibkMwP
_mm_setzero_*()
函數而不是_mm_set1_*(0)
沒有區別。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.