我有以下针对整数数组的内核矢量化:
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];
第二个内核无法工作,我不知道为什么。我知道第一种情况和第二种情况中 vector 的所有条目都是相同的(也没有溢出)。有人可以帮我找出错误吗?
谢谢
最佳答案
这是我看到的一些东西。
在
int
中和short
情况下,当您存储__m128
时至sumDot
,您使用_mm_storeu_si128
在远小于 128 位的目标上。这意味着你的内存被破坏了,很幸运你没有被咬。- 与此相关,因为
sumDot
是int[1]
即使在short
情况下,您存储了两个short
合二为一int
,然后将其读取为int
.
- 与此相关,因为
在
short
如果您缺少一个水平 vector 缩减步骤。请记住,现在您已经有了 8short
每个 vector s,您现在必须有 log_2(8) = 3 个 vector 缩减步骤。vsum = _mm_hadd_epi16(vsum, vsum); vsum = _mm_hadd_epi16(vsum, vsum); vsum = _mm_hadd_epi16(vsum, vsum);
(可选)既然您已经开始使用 SSE4.1,不妨使用它所具有的优点之一:
<罢工>PEXTR*
指示。他们获取要从中提取的车道的索引。您对底部泳道(泳道 0)感兴趣,因为这是 vector 缩减后总和的最终位置。 <罢工>罢工>
罢工>/* 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 位 vector 缩减阶段。 http://pastebin.com/A9ibkMwP- (可选)这是很好的风格,但使用
_mm_setzero_*()
没有什么区别。函数而不是_mm_set1_*(0)
.
关于c - int 与短向量化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22585306/