Bri*_*ain 14 c c++ performance sse
我想将浮点值转换为16位无符号整数而不会饱和(相反,环绕/溢出).
#include <iostream>
#include <xmmintrin.h>
void satur_wrap()
{
const float bigVal = 99000.f;
const __m128 bigValVec = _mm_set1_ps(bigVal);
const __m64 outVec64 =_mm_cvtps_pi16(bigValVec);
#if 0
const __m128i outVec = _mm_movpi64_epi64(outVec64);
#else
#if 1
const __m128i outVec = _mm_packs_epi32(_mm_cvttps_epi32(bigValVec), _mm_cvttps_epi32(bigValVec));
#else
const __m128i outVec = _mm_cvttps_epi32(bigValVec);
#endif
#endif
uint16_t *outVals = NULL;
posix_memalign((void **) &outVals, sizeof(__m128i), sizeof(__m128i));
_mm_store_si128(reinterpret_cast<__m128i *>(outVals), outVec);
for (int i = 0; i < sizeof(outVec) / sizeof(*outVals); i++)
{
std::cout << "outVals[" << i << "]: " << outVals[i] << std::endl;
}
std::cout << std::endl
<< "\tbigVal: " << bigVal << std::endl
<< "\t(unsigned short) bigVal: " << ((unsigned short) bigVal) << std::endl
<< "\t((unsigned short)((int) bigVal)): " << ((unsigned short)((int) bigVal)) << std::endl
<< std::endl;
}
Run Code Online (Sandbox Code Playgroud)
样品执行:
$ ./row
outVals[0]: 32767
outVals[1]: 32767
outVals[2]: 32767
outVals[3]: 32767
outVals[4]: 32767
outVals[5]: 32767
outVals[6]: 32767
outVals[7]: 32767
bigVal: 99000
(unsigned short) bigVal: 65535
((unsigned short)((int) bigVal)): 33464
Run Code Online (Sandbox Code Playgroud)
该((unsigned short)((int) bigVal))
表达式可以作为期望的(但它可能UB,对吧?).但我找不到与SSE非常相似的东西.我必须遗漏一些东西,但我找不到将4个32位float
s 转换为4个32位s 的原语int
.
编辑:糟糕,我认为32位整数将是"正常" - > 16位无符号整数转换使用环绕.但我已经知道_mm_packs_epi32
使用signed-saturate(并且似乎没有_mm_packus_epi32
).有没有办法设置模式,或另外一个原语_mm_packus_epi32
?
Pau*_*l R 10
我想你可能正在寻找CVTTPS2DQ
指令,其本质就是_mm_cvttps_epi32
.请参阅:http://msdn.microsoft.com/en-us/library/c8c5hx3b(v=vs.71).aspx#vcref_mm_cvttps_epi32
这是一个完整的实现,它采用2 x SSE浮点向量并将它们转换为单个打包的8 x 16位无符号向量,并带有环绕:
#include <stdio.h>
#include <tmmintrin.h>
__m128i vec_float_to_short(const __m128 v1, const __m128 v2)
{
__m128i v1i = _mm_cvttps_epi32(v1);
__m128i v2i = _mm_cvttps_epi32(v2);
v1i = _mm_shuffle_epi8(v1i, _mm_setr_epi8(0, 1, 4, 5, 8, 9, 12, 13, 255, 255, 255, 255, 255, 255, 255, 255));
v2i = _mm_shuffle_epi8(v2i, _mm_setr_epi8(255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 4, 5, 8, 9, 12, 13));
return _mm_or_si128(v1i, v2i);
}
int main(void)
{
__m128 v1 = _mm_setr_ps(0.0f, 1.0f, -1.0f, 32767.0f);
__m128 v2 = _mm_setr_ps(-32768.0f, 32768.0f, 99999.0f, -99999.0f);
__m128i v3 = vec_float_to_short(v1, v2);
printf("v1 = %vf\n", v1);
printf("v2 = %vf\n", v2);
printf("v3 = %vhu\n", v3);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
请注意,这使用PSHUFB
(_mm_shuffle_epi8
)需要SSSE3又名SSE3.5又称MNI(请参阅参考资料tmmintrin.h
),因此这只适用于合理的当前CPU(过去6年左右的英特尔).
$ gcc -Wall -mssse3 vec_float_to_short.c -o vec_float_to_short
$ ./vec_float_to_short
v1 = 0.000000 1.000000 -1.000000 32767.000000
v2 = -32768.000000 32768.000000 99999.000000 -99999.000000
v3 = 0 1 65535 32767 32768 32768 34463 31073
$
Run Code Online (Sandbox Code Playgroud)
请注意,并非所有版本的gcc都支持v
SIMD向量的printf 格式说明符(在本例中我在OS X上使用Apple的gcc).
我只回答了有关32位整数 - > 16位无符号整数转换的部分问题.
由于您需要一个环绕式,只需获取包含32位整数的每个双字的低位字.这些16位整数与16位未使用的数据交错,因此将它们打包成连续的数组可能很方便.最简单的方法是使用_mm_shuffle_epi8
内在(SSSE3).
如果您希望程序更具可移植性并且只需要SSE2指令集,则可以_mm_packs_epi32
使用以下方法打包值,但禁用其饱和行为:
x = _mm_slli_epi32(x, 16);
y = _mm_slli_epi32(y, 16);
x = _mm_srai_epi32(x, 16);
y = _mm_srai_epi32(y, 16);
x = _mm_packs_epi32(x, y);
Run Code Online (Sandbox Code Playgroud)
这个技巧有效,因为它执行16位值的符号扩展,这使得符号饱和成为无操作.
同样的技巧适用于_mm_packus_epi32
:
x = _mm_and_si128(x, _mm_set1_epi32(65535));
y = _mm_and_si128(y, _mm_set1_epi32(65535));
x = _mm_packus_epi32(x, y);
Run Code Online (Sandbox Code Playgroud)
这个技巧有效,因为它执行16位值的零扩展,这使得无符号饱和成为无操作.执行零扩展更容易,但需要SSE4.1指令集才能_mm_packus_epi32
使用.
使用单个指令可以打包8个16位整数:_mm_perm_epi8
.但这需要非常罕见的XOP指令集.
这里有几个关于饱和转换的词.
事实上,_mm_packus_epi32
如果您更改#include <xmmintrin.h>
为#include <smmintrin.h>
或,则可以使用内在函数#include <x86intrin.h>
.您需要CPU和编译器来支持SSE4.1扩展.
如果您没有兼容SSE4.1的CPU或编译器,或者希望您的程序更具可移植性,请_mm_packus_epi32
使用以下代码替换内在函数:
__m128i m1 = _mm_cmpgt_epi32(x, _mm_set1_epi32(0));
__m128i m2 = _mm_cmpgt_epi32(x, _mm_set1_epi32(65535));
x = _mm_and_si128(x, m1);
x = _mm_or_si128(x, m2);
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
3603 次 |
最近记录: |