使用两个 _mm_loadl_epi64 而不是一个 _mm_load_si128
using two _mm_loadl_epi64 over one _mm_load_si128
我需要使用 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
可以与内存操作数微融合,使整个从内存解包操作成为一个单一的融合域微指令。如果你的循环索引到一个数组,而不是递增指针,那么去加载 2x128b 然后解包,因为 punpcklwd xmm0, [rsi + rax]
不能微熔断。
实际上,从头开始。 punpcklo
仍然要求它的内存操作数是 16 字节对齐的。但是如果你的源数据是对齐的,你可以做一系列 punpcklo
/ punpckhi
对,具有相同的地址。
如果您的内部函数最终编译为 4 倍加载和 4 倍解包,那至少会比 2 倍加载和 4 倍解包稍微差一些。
如果不局限于SSE3,SSE4.1的PMOVZXWD xmm1, xmm2/m64
就完美了,因为它没有128b内存操作数和相应的对齐要求。
我需要使用 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
可以与内存操作数微融合,使整个从内存解包操作成为一个单一的融合域微指令。如果你的循环索引到一个数组,而不是递增指针,那么去加载 2x128b 然后解包,因为 punpcklwd xmm0, [rsi + rax]
不能微熔断。
实际上,从头开始。 punpcklo
仍然要求它的内存操作数是 16 字节对齐的。但是如果你的源数据是对齐的,你可以做一系列 punpcklo
/ punpckhi
对,具有相同的地址。
如果您的内部函数最终编译为 4 倍加载和 4 倍解包,那至少会比 2 倍加载和 4 倍解包稍微差一些。
如果不局限于SSE3,SSE4.1的PMOVZXWD xmm1, xmm2/m64
就完美了,因为它没有128b内存操作数和相应的对齐要求。