我为具有整数的数组提供了以下内核:
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的数据类型更改为短而不是 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];
这第二个内核不起作用,我不知道为什么。我知道第一种和第二种情况下矢量的所有条目都是相同的(也没有溢出)。有人能帮助我找到错误吗?
由于
答案 0 :(得分:2)
这是我看到的一些事情。
在int
和short
这两种情况下,当您将__m128
存储到sumDot
时,您对目标使用_mm_storeu_si128
是小于而不是128位。这意味着你一直在腐蚀记忆,幸运的是你没有被咬伤。
sumDot
即使在int[1]
案例中也是short
,您将两个 short
存储在一个int
中{1}},然后将其作为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
你使用{ {1}}在循环中。这只需要两个32位向量缩减级。 http://pastebin.com/A9ibkMwP
_mm_madd_epi16
函数代替_mm_setzero_*()
无效。