Optimization SSE2本质:直接访问内存

Optimization SSE2本质:直接访问内存,optimization,assembly,sse,simd,intrinsics,Optimization,Assembly,Sse,Simd,Intrinsics,许多SSE指令允许源操作数为16字节对齐的内存地址。例如,各种(un)包装说明。PUNCKLBW有以下签名: PUNPCKLBW xmm1,xmm2/m128 现在,这似乎根本不可能与内在的。看起来必须使用_mm_load*intrinsic读取内存中的任何内容。这是PUNPCKLBW的固有特性: __m128i-mm-EXPLO-epi8(m128i-a和m128i-b) (据我所知,_m128i类型始终引用XMM寄存器。) 为什么会这样?这是相当悲哀的,因为我通过直接寻址内存看到了一些优化潜

许多SSE指令允许源操作数为16字节对齐的内存地址。例如,各种(un)包装说明。PUNCKLBW有以下签名:

PUNPCKLBW xmm1,xmm2/m128

现在,这似乎根本不可能与内在的。看起来必须使用_mm_load*intrinsic读取内存中的任何内容。这是PUNPCKLBW的固有特性:

__m128i-mm-EXPLO-epi8(m128i-a和m128i-b)

(据我所知,_m128i类型始终引用XMM寄存器。)


为什么会这样?这是相当悲哀的,因为我通过直接寻址内存看到了一些优化潜力…

内部函数相对直接地对应于实际指令,但编译器没有义务发出相应的指令。优化加载后的操作(即使是在内部函数中写入的操作)到操作的内存形式是所有值得尊敬的编译器在有利的情况下执行的常见优化

TLDR:在intrinsic中编写加载和操作,并让编译器对其进行优化

编辑:简单示例:

#include <emmintrin.h>
__m128i foo(__m128i *addr) {
    __m128i a = _mm_load_si128(addr);
    __m128i b = _mm_load_si128(addr + 1);
    return _mm_unpacklo_epi8(a, b);
}

看到了吗?优化器将对其进行排序。

您可以直接使用内存值。例如:

__m128i *p=static_cast<__m128i *>(_aligned_malloc(8*4,16));

for(int i=0;i<32;++i)
    reinterpret_cast<unsigned char *>(p)[i]=static_cast<unsigned char>(i);

__m128i xyz=_mm_unpackhi_epi8(p[0],p[1]);

<>这是编译器做的一个很差的工作,或者这种方式更快,和/或玩这些选项会修复它——但是它生成的代码是有效的,而C++代码正直接陈述它想要的东西。这很容易用-S选项检查。我发现几乎所有内在函数都有逐字的内在函数->汇编翻译,并且可以将寄存器直接映射到变量。看起来这些编译器很难优化SIMD内部代码…@dieter:clang和gcc都进行了优化,如我的示例所示。您是在关闭优化的情况下构建的吗?尝试使用
-O1
或更高版本。我正在使用-O2。我猜gcc/clang在我的特定代码中根本看不到任何优化潜力。。。
__m128i *p=static_cast<__m128i *>(_aligned_malloc(8*4,16));

for(int i=0;i<32;++i)
    reinterpret_cast<unsigned char *>(p)[i]=static_cast<unsigned char>(i);

__m128i xyz=_mm_unpackhi_epi8(p[0],p[1]);
; __m128i xyz=_mm_unpackhi_epi8(p[0],p[1]);
0040BC1B 66 0F 6F 00      movdqa      xmm0,xmmword ptr [eax] 
0040BC1F 66 0F 6F 48 10   movdqa      xmm1,xmmword ptr [eax+10h] 
0040BC24 66 0F 68 C1      punpckhbw   xmm0,xmm1 
0040BC28 66 0F 7F 04 24   movdqa      xmmword ptr [esp],xmm0