2016-09-21 6 views
1

クエリ応答プログラムを高速化するためにIntel SIMD組み込み関数を使用しようとしています。 query_cntが入力に依存するが、常にSIMDレジスタの数よりも少ない(すなわち、それらを保持するのに十分なSIMDレジスタがある)と仮定する。クエリはアプリケーションのホットなデータなので、必要なときに毎回ロードするのではなく、最初にロードして常にレジスタに保持してもよいでしょうか?SIMD組み込み関数を使用するときに入力依存ホットデータをレジスタに保持する方法

クエリがfloatタイプで、AVX256がサポートされているとします。私はそれが潜在的なロード/ストアのオーバーヘッドがあるので、良い習慣ではありません知っているが、少なくとも、彼らができるようにvec_queries[i]を最適化することができ、わずかチャンスがある

std::vector<__m256> vec_queries(query_cnt/8); 
for (int i = 0; i < query_cnt/8; ++i) { 
    vec_queries[i] = _mm256_loadu_ps((float const *)(curr_query_ptr)); 
    curr_query_ptr += 8; 
} 

:今、私のようなものを使用する必要があります私はまだそれが良い方法ではないと思う。

もっと良いアイデアを教えてください。

+0

何もしないループで複数のクエリを処理していますか?そうでない場合は、次のクエリを取得したときにデータが登録されません。あるいは、クエリにグローバルレジスタ変数を使用する価値があると思っていますか? GNU Cはこれを行うことができます。 '__m256 vec_query0 asm(" ymm0 ");は正しい構文IIRCでなければなりません。 –

+0

ベクトル*がレジスタに保持されていないことを確認するために実際のasmを見ましたか?あなたが運が良ければ、コンパイラはstd :: vectorの動的割り当てオーバーヘッドのほとんどを最適化しているかもしれません。そうでない場合は、固定サイズの配列を使用してみてください(サイズの上限が低いため)。 –

+0

@PeterCordesあなたのアドバイスをありがとう。私は実際のasmを見ていませんでしたが、固定サイズの配列を使うことは良いオプションかもしれないので、すべての配列要素をレジスタに束縛するために '__m256 vec_query0 asm(" ymm0 ")'のようなものを使うことができます私がそうするならば、いくつかのレジスタは常に固定要素によって占有され、パフォーマンス上のペナルティを招くでしょうか? – MarZzz

答えて

0

投稿したコードサンプルから、あなたは可変長のmemcpyをやっているようです。コンパイラの動作とその周辺のコードによっては、実際にmemcpyを呼び出すだけで良い結果が得られるかもしれません。例えばベクトル・ループとrep movsbとの間のブレーク偶数ポイントは、Intel Haswellでは〜128バイトになる可能性があります。 memcpyに関するいくつかのインプリメンテーションノートと、いくつかの異なる戦略のサイズとサイクルのグラフについては、インテルの最適化マニュアルをチェックしてください。 (タグwikiのリンク)

あなたはどのCPUを言っていないので、私は最近のインテルを想定しています。

あなたはレジスタについてあまりにも心配していると思います。 L1キャッシュでヒットした負荷は非常に安いです。 Haswell(およびSkylake)は、クロックごとに2つの__m256ロード(および同じサイクル内のストア)を実行できます。それ以前は、Sandybridge/IvyBridgeはクロックごとに2つのメモリ操作を行うことができ、そのうちの1つはストアとなりました。また、理想的な条件(256bのロード/ストア)では、1つのクロックで2倍の16Bロードと1倍の16Bを管理できます。したがって、256bベクタをロード/格納するのはHaswellよりも高価ですが、L1キャッシュで整列していれば非常に安いです。

私はコメントで、GNU C global register variablesが可能性がありますが、主に「これは理論的には技術的に可能です」という意味で述べました。プログラムの実行時間全体(ライブラリ関数呼び出しを含むので、再コンパイルする必要があるでしょう)には、この目的に専用の複数のベクトルレジスタが必要ではないでしょう。

実際には、重要なループ内で使用するすべての関数の定義をコンパイラがインラインで(または少なくとも最適化しているのを見て)確認できることを確認してください。そうすれば、WindowsとSystem V x86-64 ABIの両方に呼び出し保存YMM(__m256)レジスタがないため、ファンクションコール間でベクトルレジスタをスピル/リロードする必要がなくなります。

最新のCPUのマイクロアーキテクチャの詳細、少なくとも実験で測定して調整することが可能な詳細については、Agner Fog's microarch pdfを参照してください。

+0

私は '__m256 a'と' float b [8] 'の間に' memcpy'を試して '_mm256_loadu_ps'と' _mm256_storeu_ps'を模倣し、本当にうまくいくことを発見しました。最初は、 '__m256'や' __m256i'のようなSIMDの本質的なデータ型は、レジスタに保存される可能性の高い特殊なデータ型だと私は思っていました。今私は、彼らが 'float'や' int'のような相手に比べて特別なものではないように思えますが、より多くの配置制限があるのは正しいですか?もしそうなら、すべてのSIMD 'load/stores'を' memcpy'に置き換えれば、パフォーマンスの違いは何でしょうか? – MarZzz

+0

@MarZzz:はい、スカラー型の 'float'や' int'型のように動作します。より多くの計算に '__m256'(または' __m256i')を使用しようとするならば、ロード組み込み関数を使用してください。 (または別の '__m256'の簡単な割り当てを使用してください)。たくさんのデータを移動したい場合は、memcpyを使います。 memcpyを使って 'array [i]'を 'float tmp'に代入することはありません。したがって、SIMD型に対してはしないでください。 *おそらく*最適化されますが、読みにくく、コンパイラにはあまり役立ちません。 –

+0

このようなことが不思議なら、コンパイラの出力を見てください。コンパイラが生成したいくつかのコードを、最初から自分のアセンブリを書くことよりもソートする方がはるかに簡単なので、実際にこれを試してみる必要はありません。 http://gcc.godbolt.org/にいくつかのコードを書いて[うまくフォーマットされたビュー]を得る(http://stackoverflow.com/questions/38552116/how-to-remove-noise-from-gcc-clang-アセンブリー出力)を指定します(どのソース行がどのasm行を生成したかを示すためにオプションの色の強調表示が付いています)。 –

関連する問題