【发布时间】:2015-08-31 10:57:30
【问题描述】:
我想将一个双精度值向量转换为 char。 我必须采用两种不同的方法,一种用于 SSE2,另一种用于 AVX2。
我从 AVX2 开始。
__m128i sub_proc(__m256d& in)
{
__m256d _zero_pd = _mm256_setzero_pd();
__m256d ih_pd = _mm256_unpackhi_pd(in,_zero_pd);
__m256d il_pd = _mm256_unpacklo_pd(in,_zero_pd);
__m128i ih_si = _mm256_cvtpd_epi32(ih_pd);
__m128i il_si = _mm256_cvtpd_epi32(il_pd);
ih_si = _mm_shuffle_epi32(ih_si,_MM_SHUFFLE(3,1,2,0));
il_si = _mm_shuffle_epi32(il_si,_MM_SHUFFLE(3,1,2,0));
ih_si = _mm_packs_epi32(_mm_unpacklo_epi32(il_si,ih_si),_mm_unpackhi_epi32(il_si,ih_si));
return ih_si;
}
__m128i proc(__m256d& in1,__m256d& in2)
{
__m256d _zero_pd = _mm_setzeros_pd();
__m128i in1_si = sub_proc(in1);
__m128i in2_si = sub_proc(in2);
return _mm_packs_epi16(in1_si,in2_si);
}
int main()
{
double input[32] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32};
char output[32] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
char check[8];
double* ibeg = input;
char* obeg = output;
for(int i=0;i<32;i+=8)
{
__m256d in1 = _mm256_loadu_pd(ibeg);
__m256d in2 = _mm256_loadu_pd(ibeg+4);
__m128i tmp = proc(in1,in2);
_mm_storeu_si128(reinterpret_cast<__m128i*>(check),tmp);
std::copy(check,check+8,std::ostream_iterator<int>(std::cout," "));
std::cout<<std::endl;
_mm_storeu_si128(reinterpret_cast<__m128i*>(obeg+i),tmp);
}
}
在这个算法的最后,输出包含:
1,2,3,4,0,0,0,0,9,10,11,12,0,0,0,0,17,18,19,20,0,0,0,0,25,26,27,28,0,0,0,0
我的第一次调查显示,如果在proc 函数中我改变了:
return _mm_packs_epi16(in1_si,in2_si);
到:
return _mm_packs_epi16(in2_si,in1_si);
那么输出包含:
5,6,7,8,0,0,0,0,13,14,15,16,0,0,0,0,21,22,23,24,0,0,0,0,29,30,31,31,0,0,0,0
我还没有弄清楚如何洗牌in2_si 的高低部分。
有没有更好(更快、更有效)的方法来使用 SIMD 将双精度数字转换为 char?
【问题讨论】:
-
一定会喜欢一些指令如何跨越 128b 通道(就像
VCVTPD2DQ一样),而其他指令(打包/解包)则执行两个单独的 128b 操作。我猜这些洗牌是以为你在每个 128b 通道的低 64 位有两个 64 位结果?不过,unpack-with-zero 仍然没有意义。 >.