行列とベクトルの乗算を1秒間に240000回実行する必要があります。行列は5x5であり、常に同じですが、ベクトルは反復ごとに変化します。データ型はfloat
です。私はいくつかのSSE(または同様の)命令を使用することを考えていました。
関係するメモリ操作の数と比較して、算術演算の数が少なすぎるのではないかと心配しています。私は具体的な(たとえば> 20%)改善を得ることができると思いますか?
それを行うにはIntelコンパイラが必要ですか?
いくつかの参考文献を指摘できますか?
行列とベクトルの乗算を1秒間に240000回実行する必要があります。行列は5x5であり、常に同じですが、ベクトルは反復ごとに変化します。データ型はfloat
です。私はいくつかのSSE(または同様の)命令を使用することを考えていました。
関係するメモリ操作の数と比較して、算術演算の数が少なすぎるのではないかと心配しています。私は具体的な(たとえば> 20%)改善を得ることができると思いますか?
それを行うにはIntelコンパイラが必要ですか?
いくつかの参考文献を指摘できますか?
ベクトル、行列、...用のEigenC ++テンプレートライブラリには両方があります
小さな固定サイズの行列(および動的なサイズの行列)用に最適化されたコード
SSE最適化を使用する最適化されたコード
だからあなたはそれを試してみるべきです。
原則として、スピードアップはSSEで4倍(AVXで8倍)になる可能性があります。説明させてください。
固定5x5マトリックスをMと呼びましょう。5Dベクトルのコンポーネントを(x、y、z、w、t)として定義します。ここで、最初の4つのベクトルから 5x4行列Uを形成します。
U =
xxxx
yyyy
zzzz
wwww
tttt
次に、行列積MU=Vを実行します。行列Vには、 Mと最初の4つのベクトルの積が含まれています。唯一の問題は、SSEの場合、Uの行を読み取る必要がありますが、メモリにUがxyzwtxyzwtxyzwtxyzwtとして格納されるため、 xxxxyyyyzzzzwwwwttttに転置する必要があることです。これは、SSEのシャッフル/ブレンドを使用して実行できます。この形式になると、行列積は非常に効率的になります。
スカラーコードでO(5x5x4)演算を実行する代わりに、O(5x5)演算、つまり4倍の高速化のみを実行します。AVXでは、マトリックスUは5x8になるため、O(5x5x8)の操作を行う代わりに、O(5x5)にのみ課税します。つまり、8倍のスピードアップになります。
ただし、行列Vはxxxxyyyyzzzzwwwwtttt形式になるため、アプリケーションによっては、xyzwtxyzwtxyzwtxyzwt形式に置き換える必要がある場合があります。
完了するまで、次の4つのベクトル(AVXの場合は8)についてこれを繰り返します。
ベクトルを制御できる場合、たとえば、アプリケーションがその場でベクトルを生成する場合は、それらをxxxxyyyyzzzzwwwwtttt形式で生成し、配列の転置を回避できます。その場合、SSEで4倍、AVXで8倍の速度が得られるはずです。これをOpenMPなどのスレッド化と組み合わせると、SSEを使用した場合のスピードアップは16倍近くになります(4つの物理コアを想定)。それがSSEでできる最善のことだと思います。
編集:命令レベルの並列性(ILP)により、スピードアップでさらに2倍の速度が得られるため、SSEのスピードアップは4コア(64x AVX)で32倍になり、FMA3によりHaswellでさらに2倍になります。
Intel IPPを使用して、テクニックへの依存を抽象化することをお勧めします
GCCを使用している場合は、-O3オプションを使用すると自動ベクトル化が有効になり、多くの場合、SSEまたはAVX命令が自動的に生成されることに注意してください。一般に、単純なforループとして記述するだけで、GCCはそれをベクトル化します。詳細については、http: //gcc.gnu.org/projects/tree-ssa/vectorization.htmlを参照してください。
_mm_dp_ps
これは、特にCore 2以降を使用している場合は簡単です。5* 、1、2、1_mm_mul_ps
つ_mm_add_ps
の通常の乗算に加えて、いくつかのシャッフル、ロード、およびストアが必要です(マトリックスが固定されている場合は、ほとんどを保持できます) SSEレジスタでは、他に必要がない場合)。
メモリ帯域幅について:メモリ帯域幅が1桁のギガバイト/秒の場合、2.4メガバイトのベクトルについて話します。
ベクトルについて何が知られていますか?行列は固定されており、ベクトルが取ることができる値の量が限られている場合は、計算を事前に計算し、テーブルルックアップを使用してそれらにアクセスすることをお勧めします。
メモリをサイクルと交換するための古典的な最適化手法...
IntelMKLやAMDACMLなどの最適化されたBLASライブラリを確認することをお勧めします。あなたの説明に基づいて、私はあなたがスタイル操作SGEMV
をするために、レベル2の行列-ベクトルルーチンの後にいると思います。y = A*x
本当に自分で何かを実装したい場合は、(使用可能な)命令セットを使用するSSE..SSE4
とAVX
、パフォーマンスが大幅に向上する場合がありますが、これはまさに優れたBLASライブラリが実行することです。また、キャッシュに適したデータアクセスパターンについてもよく考える必要があります。
これがあなたの場合に当てはまるかどうかはわかりませんが、一度にベクトルの「チャンク」を操作できますか?したがって、スタイル操作を繰り返し実行するのではなく、y = A*x
のブロックを操作できます[y1 y2 ... yn] = A * [x1 x2 ... xn]
。その場合、これは、などの最適化された行列-行列ルーチンを使用できることを意味しますSGEMM
。データアクセスパターンにより、これはへの繰り返しの呼び出しよりも大幅に効率的である可能性がありますSGEMV
。もし私だったら、この道を進んでみようと思います...
お役に立てれば。
ベクトルを事前に知っている場合(たとえば、一度にすべての240kを実行する場合)、SSEに移動するよりも、ループを並列化する方が高速化されます。すでにそのステップを踏んでいる場合、または一度にすべてを知らない場合は、SSEが大きなメリットになる可能性があります。
メモリが隣接している場合は、メモリ操作についてあまり心配する必要はありません。あなたがリンクリストか何かを持っているなら、あなたは困っています、しかしそれはあまり問題なく追いつくことができるはずです。
5x5はおかしなサイズですが、1つのSSE命令で少なくとも4フロップスを実行し、算術オーバーヘッドを削減しようとすることができます。Intelコンパイラは必要ありませんが、もっと良いかもしれません。算術コードを使用した方がはるかに優れているという伝説を聞いたことがあります。Visual StudioにはSSE2を処理するための組み込み関数があり、必要なものに応じてSSE4までだと思います。もちろん、あなたはそれを自分で転がさなければならないでしょう。ここでは、ライブラリを取得するのが賢明かもしれません。