2

多くの SSE 命令では、ソース オペランドを 16 バイト アラインされたメモリ アドレスにすることができます。たとえば、さまざまな (un) pack 命令。PUNCKLBW には次の署名があります。

PUNPCKLBW xmm1, xmm2/m128

現在、これは組み込み関数ではまったく可能ではないようです。_mm_load* 組み込み関数を使用してメモリ内の何かを読み取ることが必須のようです。これは、PUNPCKLBW の組み込みです。

__m128i _mm_unpacklo_epi8 (__m128i a、__m128i b);

(私の知る限り、__m128i 型は常に XMM レジスタを参照します。)

さて、これはなぜですか?メモリを直接アドレス指定することで最適化の可能性が見られるので、かなり悲しいです...

4

2 に答える 2

7

組み込み関数は実際の命令に比較的直接対応していますが、コンパイラは対応する命令を発行する義務はありません。読み込みの後に操作が続く (組み込み関数で記述されている場合でも) 操作のメモリ形式への最適化は、そうすることが有利な場合に、すべての立派なコンパイラによって実行される一般的な最適化です。

TLDR: 読み込みと操作を組み込み関数に記述し、コンパイラに最適化させます。

編集:簡単な例:

#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);
}

でコンパイルすると、次のようになりgcc -Os -fomit-frame-pointerます。

_foo:
movdqa      (%rdi), %xmm0
punpcklbw 16(%rdi), %xmm0
retq

見る?オプティマイザはそれを整理します。

于 2010-07-28T20:08:49.540 に答える
3

メモリ値を直接使用できます。例えば:

__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 

そのため、コンパイラは少しお粗末な仕事をしています - または、おそらくこの方法の方が高速であり、オプションをいじることでそれが修正されるでしょう - しかし、コンパイラは機能するコードを生成し、C++ コードは必要なものをかなり直接的に記述しています。

于 2010-08-01T17:06:04.933 に答える