2012-02-16 5 views
8

GCCのvector extensionsは、hardware specific intrinsics(または自動ベクトル化)に頼らずに、異なるハードウェアアーキテクチャ上のいくつかのSIMD命令にアクセスするうえで便利で便利な方法を提供します。GCCのベクトル拡張のデータを読み込む

実際の使用例では、単純な加算チェックサムを計算しています。明らかでない1つのことは、安全にデータをベクトルにロードする方法です。ベクトル型へのポインタをキャスト

typedef char v16qi __attribute__ ((vector_size(16))); 

static uint8_t checksum(uint8_t *buf, size_t size) 
{ 
    assert(size%16 == 0); 
    uint8_t sum = 0; 

    vec16qi vec = {0}; 
    for (size_t i=0; i<(size/16); i++) 
    { 
     // XXX: Yuck! Is there a better way? 
     vec += *((v16qi*) buf+i*16); 
    } 

    // Sum up the vector 
    sum = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5] + vec[6] + vec[7] + vec[8] + vec[9] + vec[10] + vec[11] + vec[12] + vec[13] + vec[14] + vec[15]; 

    return sum; 
} 

が動作しているように見えますが、私は、SIMDハードウェアがベクトル型が正しく整列されることを想定した場合、これは恐ろしい方法で爆発する可能性が心配です。

私が考えた唯一の他の選択肢は、一時ベクトルを使用して明示的に(memcpyまたは要素ごとの割り当てによって)値をロードすることですが、この反作用のテストではほとんどの高速化がSIMD命令の使用を得ました。理想的には、これは汎用の__builtin_load()のようなものだと思いますが、存在しないようです。

アライメントの問題を起こす可能性のあるベクターにデータをロードするより安全な方法は何ですか?

+2

GCC x86_64での非整列メモリ上でこれ​​を実行すると発生しますCPUがソートされていないメモリをSSEレジスタにロードしようとすると、SIGSEGV。 1つの合理的なオプションは、チェックサムアライメントされたメモリだけであるか、通常のループを使用して最初の16バイト境界までバイトを合計します。 – dcoles

+0

現在のコードでは、データのロードはコンパイラが入力を知っていれば(実際は問題ありません)、実際にはうまくコンパイルされます:https://godbolt.org/g/DeR3Qv。 https:// godboltという入力を知らなくてもそれほど素晴らしいことではありません。org/g/LxEkhp – ZachB

答えて

0

あなたはすなわち

const vec16qi e = { buf[0], buf[1], ... , buf[15] } 

を行うと、GCCは、SSEのロード命令にこれを変わることを願って、値をロードするために初期化子を使用することができます。私は逆アセンブラでそれを確認したいと思いますが、;-)。また、パフォーマンスを向上させるために、bufを16バイトに整列させて、aligned属性でコンパイラに通知してください。入力バッファが確実にアライメントされるようにするには、16バイト境界に達するまでバイト単位で処理します。

+0

私はbufを整列する必要はないと思います。私たちが指針を扱っていれば、それがあります。 – user1095108

+0

@ user1095108コンパイラにこれをSSEロード命令にしてもらいます。これは 'e = * buf'に相当します(ただし、型が一致しないので、そのように書くことはできません)。実際にここでポインタを扱っています。コンパイラがbufが16バイトでアライメントされていると推測できる場合、整列されたロードを使用することができます(あらかじめivy-bridge以上)。 – fgp

+0

いいえ、私の経験から 'buc'を' vec16qi'にキャストするのであれば、ポインタを扱うでしょう。 – user1095108

1

編集(おかげでピーター・コルド)あなたは、ポインタをキャストすることができます

typedef char v16qi __attribute__ ((vector_size (16), aligned (16))); 

v16qi vec = *(v16qi*)&buf[i]; // load 
*(v16qi*)(buf + i) = vec; // store whole vector 

これは、ストアにロードし、vmovupsするvmovdqaにコンパイルされます。データがアライメントされていない場合は、vmovdquを生成するようにaligned (1)を設定します。そこにいくつかの特別な目的の組み込みコマンドのロードにもあり、これらのレジスタ(編集2)をアンロードすること(godbolt

注:

v16qi vec = _mm_loadu_si128((__m128i*)&buf[i]); // _mm_load_si128 for aligned 
_mm_storeu_si128((__m128i*)&buf[i]), vec); // _mm_store_si128 for aligned 

char sから行くために-flax-vector-conversionsを使用する必要があると思われますこの機能を使用するとv16qiになります。

参照:C - How to access elements of vector using GCC SSE vector extension
も参照してください:SSE loading ints into __m128

(ヒント:Googleに最高のフレーズは "gccの負荷__m128i" のようなものである)

+1

アラインメントのないデータをGNU Cのベクトルにロードするための推奨される方法は、ベクトル型を宣言する際に 'aligned(1)'属性を使用し、そのアラインされていないベクトル型へのポインタをキャストすることです。例えば'typedef char __attribute__((vector_size(16)、aligned(1)))unaligned_byte16;'。 [私の答えの終わり](http://stackoverflow.com/a/39115055/224132)とMarc Glisseのコメントを参照してください。 –

+0

@PeterCordesありがとう!編集された答えは、はるかに簡単です。 – ZachB

+0

抽出するには、あなたは 'vec [0]'を使うべきだと思います。私が理解しているように、ベクトル型へのスカラーポインタのエイリアシングは* ok *ではありません。 'char *'は 'char *'が特別なので何でも使えます。 v4siは 'int'で定義されているので、' int * 'を' v4si * 'にキャストすることはエイリアシングとしてカウントされません。インテルの組み込み関数型( '__m128i')は余分な属性のために別名にもエイリアスできます:' typedef long long __m128i __attribute__((__vector_size__(16)、__may_alias __))) 'may_aliasがなければ、' v4si ivec = *(v4si)short_pointer'。私はその前にそれを残しました –

関連する問題