我需要使用 16 位值(正值)并将它们提升到 32 位。
使用 SIMD(我仅限于 SSE3),这是我想出的两个选项:
reg_xmm0 = _mm_loadu_si128((const __m128i *)(Src));
reg_xmm2 = _mm_loadu_si128((const __m128i *)(Src+8));
reg_xmm1 = _mm_unpackhi_epi16(reg_xmm0,_mm_setzero_si128());
reg_xmm0 = _mm_unpacklo_epi16(reg_xmm0,_mm_setzero_si128());
reg_xmm3 = _mm_unpackhi_epi16(reg_xmm2,_mm_setzero_si128());
reg_xmm2 = _mm_unpacklo_epi16(reg_xmm2,_mm_setzero_si128());
或者我可以这样做,
reg_xmm0 = _mm_loadl_epi64((const __m128i *)(Src));
reg_xmm1 = _mm_loadl_epi64((const __m128i *)(Src+4));
reg_xmm2 = _mm_loadl_epi64((const __m128i *)(Src+8));
reg_xmm3 = _mm_loadl_epi64((const __m128i *)(Src+12));
reg_xmm0 = _mm_unpacklo_epi16(reg_xmm0,_mm_setzero_si128());
reg_xmm1 = _mm_unpacklo_epi16(reg_xmm1,_mm_setzero_si128());
reg_xmm2 = _mm_unpacklo_epi16(reg_xmm2,_mm_setzero_si128());
reg_xmm3 = _mm_unpacklo_epi16(reg_xmm3,_mm_setzero_si128());
我应该选择哪种方法?使用第二种方法比第一种方法有任何性能改进吗?请注意,我已经更换了
_mm_loadu_si128
带两个 _mm_loadl_epi64
.
最佳答案
大多数情况下,您需要上下文来说明某些事情是更快还是更慢。延迟、执行端口或 uop 吞吐量(前端)都是常见的瓶颈。
如果您使用 1 寄存器寻址模式,punpcklo
可以与内存操作数进行微融合,使整个从内存中解包的操作成为单个融合域 uop。如果你的循环索引到一个数组,而不是增加指针,那么去加载 2x128b 然后解包,因为 punpcklwd xmm0, [rsi + rax]
不能微保险。
实际上,从头开始。 punpcklo
仍然需要它的内存操作数是 16 字节对齐的。但是如果你的源数据是对齐的,你可以做一系列 punpcklo
/punpckhi
对,具有相同的地址。
如果您的内部函数最终编译为 4x 加载和 4x 解包,那至少会比 2x 加载和 4x 解包略差。
如果您不仅限于 SSE3,SSE4.1 的 PMOVZXWD xmm1, xmm2/m64
将是完美的,因为它没有具有相应对齐要求的 128b 内存操作数。
关于c - 在一个 _mm_load_si128 上使用两个 _mm_loadl_epi64,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28829951/