2016-10-05 19 views
2

私はSIMDの組み込み関数の初心者ですから、あらかじめ忍耐強くお礼を申し上げます。私は符号なしバイトの絶対差分比較を含むアプリケーションを持っています(私はグレースケール画像で作業しています)。SSE2 __m128i構造体からバイトを抽出する方法は?

私はAVX、より近代的なSSEバージョンなどを試しましたが、最終的にはSSE2で十分と思われ、個々のバイトを最大限サポートしています。間違っていると私を修正してください。

私は2つの質問があります:まず、128ビットレジスタをロードする正しい方法は何ですか?私は、私は128の倍数に整列ロード組み込み関数のデータを渡すことになってると思いますが、このような2次元配列のコードでその仕事になります。

greys = aligned_alloc(16, xres * sizeof(int8_t*)); 

for (uint32_t x = 0; x < xres; x++) 
{ 
    greys[x] = aligned_alloc(16, yres * sizeof(int8_t*)); 
} 

は、(上記のコードはXRESとyresが同じであると仮定し、としています2のべき乗)。これはメモリ内の線形で途切れのないブロックになりますか?私はループして、SSE2のロードイントリンシクスにアドレスを渡し続けます(128をインクリメントする)ことができますか?それとも、このような2D配列のために何か別の必要性がありますか?

私のすべてのベクトル処理が完了したら、__m128iから変更されたバイトをどのように抽出するのですか?インテルイントリンシックスガイドを見て、ベクトル型をスカラー型に変換する手順はまれです。私が見つけた最も近いのは_mm_movemask_epi8 (__m128i a)ですが、どうやって使うのか分かりません。

ああ、3分の1質問 - 私は_mm_load_si128が符号付きバイトだけを読み込むと仮定しましたか?そして、私は他のバイトローディング関数を見つけることができませんでした。だから、それぞれから128を引いて、後でそれを説明すると思いますか?

私はこれらがSIMDの専門家のための基本的な質問であることを知っていますが、私はこの1つが私のような初心者に役立つことを願っています。アプリケーションへの私の全体的なアプローチが間違っていると思うのであれば、私はもっと近代的なSIMDエクステンションを使う方が良いと思っています。私は謙虚に私がアセンブリで働いたことはないと警告したいと思います。そして、この手の込んだすべてのものは、私を助けるためには多くの説明が必要です。

それにもかかわらず、私は利用可能なすべての説明に感謝しています。

違いがある場合:私は、低電力i7 Skylakeアーキテクチャをターゲットにしています。しかし、アプリケーションをずっと古いマシンで実行させるのもいいでしょう(それゆえSSE2)。最初

答えて

4

最小明白な疑問:

私はすべての私のベクトル処理を完了したら、一体私は__m128i

から変更されたバイトを抽出んどのように低い64ビットを抽出整数はint64_t _mm_cvtsi128_si64x(__m128i)、またはthe low 32 bits with int _mm_cvtsi128_si32 (__m128i a)である。あなたは、ベクターの他の部分をしたい場合は、あなたのオプションは次のとおりです。

  • あなたが低い要素にしたいデータを新しい__m128iを作成し、CVTの組み込み関数(ASMでMOVDやMOVQ)を使用するベクトルをシャッフルします。
  • SSE2 int _mm_extract_epi16 (__m128i a, int imm8)、または他の要素サイズについてはSSE4.1同様の命令を使用してください。 PEXTRB/W/D/Qは最速の命令ではありませんが、上位の要素が1つだけ必要な場合は、別のシャッフルとMOVDよりも優れています。
  • 一時配列に格納するか、union { __m128i v; int64_t i64[2]; }などを使用します。ユニオンベースのタイプのペニングはC99では有効ですが、C++の拡張としてのみ有効です。 C++で動作するユニオンの代わりに、memcpy(&my_int64_local, 8 + (char*)my_vector, 8);があり、上位半分を抽出できます。うまくいけば、それはストア+実際のmemcpyライブラリ関数呼び出しにコンパイルされません。コンパイラは通常、小規模な固定サイズのmemcpyをこのようなユースケースのために最適化するのにはかなり良いですが、コンパイラが-msse4.1でコンパイルしても、必要に応じてコンパイラが使用できるようにしても、おそらくPEXTRQではなくストア/ 。結果は、メモリ変更されていない(代わりの整数レジスタに必要とされている)に直接行くことができる場合は、スマートコンパイラは__m128i

の高い半分を保存するためにMOVHPSを使うには、このターンにいかもしれませんメモリ内の線形で途切れていないブロック?

いいえ、それはメモリの別々のブロックへのポインタの配列であり、適切な2D配列とは別のレベルの間接参照を導入しています。それをしないでください。

大きな割り当てを1つ作成し、インデックス計算を自分で行います(array[x*yres + y]を使用)。

はい、オフセットデータからロードする必要がある場合は_mm_load_si128、またはloaduを使用してデータをロードしてください。


_mm_load_si128を前提と署名バイト

署名

または符号なしバイトの固有のプロパティではありませんロードし、それはあなたがビットを解釈する方法のみです。 2つの64ビット要素、または128ビットのビットマップをロードするために、同じロード組み込み関数を使用します。

データに適した組み込み関数を使用します。これはアセンブリ言語のようなものです。すべてが単なるバイトであり、マシンはバイトでそれを伝えます。意味のある結果をもたらす一連の命令/組み込み関数を選択するのはあなた次第です。

整数ロード組み込み関数は__m128i*ポインタの引数をとるので、_mm_load_si128((const __m128i*) my_int_pointer)などを使用する必要があります。これは、ポインタエイリアシング(例えば、からshort *の配列を読み取る)のように見えます。これはCおよびC++の未定義動作です。しかし、これがインテルの言う通りです。インテルのイントリンシックを実装しているコンパイラであれば、これを正しく動作させる必要があります。 gccは__m128i__attribute__((may_alias))と定義することによってそうする。

Loading data for GCC's vector extensionsも参照してください。これは、インテルイントリンシックスをGNU Cネイティブベクター拡張に使用でき、ロード/ストアする方法を示しています。


SSEとSIMDの詳細については、いくつかのイントロ/チュートリアルリンクを含むタグウィキでいくつかのリンクがあります。

タグwikiには、いくつかの優れたx86 asm/performanceリンクがあります。

+1

他のオプションは、コンパイラが最初にベクトル化するかどうかを確認し、次にコンパイラが実行できる処理がさらに必要な場合は、後でイントリンシックを追加します。理想的には私たちはすべてバニラコードを使用し、コンパイラライターが最適化するために設定したものであれば、コンパイラはSSE、AXVなどをサポートします。 – Holmz

+0

@Peter Cordez - 残念ながら、私は16バイトのベクトルにバイトをロードする方法を見つけることさえできません。 load/storeコマンドはすべて、アドレスを含む結果と引数の両方のベクトルを使用しているようです!私の直感は、私はバイト配列からアドレスを(ロード引数として)提供していましたが、ロード組み込み関数はベクトルを引数として必要とするため不可能です。そして、私は、通常の、スカラーの64ビットアドレスから(アドレス)ベクトルを返す組み込み関数を見つけることができません! – sacheie

+0

@sacheie:そうですね、整数の場合は、_mm_loadu_si128((const __m128i *)my_int_pointer) 'を使う必要があります。タグwikiからリンクされているチュートリアルのいずれかを見れば、これを見つけたでしょう。 –

関連する問題