· 

第20回 ベクトル演算、SIMD演算向けのコード生成~自動ベクトル化

前回紹介したベクトル(SIMD)演算器に向けたコンパイル技術が自動ベクトル化です。自動ベクトル化は自動並列化(16回参照)と似たような概念であり、広い意味では自動ベクトル化は自動並列化の一部ですが、狭い意味では異なります。自動並列化は複数のプロセッサ向けに処理を分割し、タスクやスレッドとして実行コードを生成する手法であるのに対し、自動ベクトル化はベクトル(SIMD)演算器に向けた機械語命令(例えば前回の図の中のアセンブリ言語命令simdadd)を含む実行コードを自動生成する手法であるためです。最近のgccclangなどのコンパイラは自動ベクトル化機能を有しており、例えばIntelプロセッサの各コアに搭載されているSIMD演算器(SSE等)向けに実行コードを自動生成します。この場合、自動ベクトル化機能は複数のコアは使いません。

 

(狭義の)自動並列化と自動ベクトル化は排他的ではありません。自動並列化はタスクやスレッドといったオーバーヘッドが大きい並列化を扱うため処理単位を大きく取る方が効率的であり、自動ベクトル化は機械語命令レベルでの並列処理となります。そのため、どのレベルでも並列化が可能な多重ループがあるとき、最外ループで自動並列化を実施し、各コアに割り当てられた最内ループで自動ベクトル化を適用するといった高速化が可能です。この話は次の機会に説明します。

 

自動的にベクトル化する手法は、基本的にループ内の実行解析や依存解析になります。同時並列数が少ないSIMD演算器はCPUの内部または密結合で実装できオーバーヘッドを極めて小さくできるため、単純なループを高速実行する自動ベクトル化を実現できます。第16回において説明した自動並列化と同様、不用意にポインタ等を使わないことが重要です。すべてのループ回転において同じ機械語命令が同じ順序で実行でき、メモリ参照はループカウンタ変数の単純な式で表現できるとき、最大性能を発揮します。大規模行列計算にはベクトル化に向いた処理が多く現われます。ベクトル型スーパーコンピュータでは、データ転送オーバーヘッドは無視できませんが専用ハードウェアによるサポートがあり、比較的依存解析が容易なFORTRAN言語を使うことが歴史的に多く、コンパイラにわかりやすく並列をプログラミングするノウハウも蓄積されているため、古くから自動ベクトル化コンパイラが利用されています。

 

一方で、ループ回転ごとに異なる実行になるような場合や、配列の間接参照や複雑なループ終了判定があるような場合、解析が簡単ではなく、思うような性能が出ない場合もあります。そのような特殊処理や例外処理をサポートするベクトル(SIMD)対応機械語命令も合わせてサポートされていることは多いですが、複数データの同一命令並列実行が基本戦略である中でこれらの機械語コードを自動生成することは簡単ではありません。ソフトウェアから見たオーバーヘッドが極めて小さい特性を利用し、数命令程度の並列化を実現しようとしているときに、場合分け等でもたついただけで性能向上しない場合も多々あります。ベクトル(SIMD)演算の性能特性や種類はハードウェアアーキテクチャごとに異なり、それゆえ手動チューニングの効果が大きいこともあり、自動ベクトル化技術はスーパーコンピュータ向けを除いてはまだまだ発展途上です。現状、組込み向けでは行列計算や画像処理、AI処理に対し、ハードウェアベンダによりチューニングされたSIMD利用ライブラリ関数が提供されている場合が多く、そのようなライブラリ関数が使える処理であれば自動ベクトル化よりも簡単に高性能を実現することができます。

 

 

他方で、組込みシステムの性能向上の多くの部分をベクトル(SIMD)的な処理が占めるようになってきており、その高速・省エネルギー実行の短TAT開発のニーズが日々高まっています。ハードウェアアーキテクチャではRISC-Vにベクトル拡張命令が定義され(下図)、コンパイラ基盤のLLVMの中間言語LLVM-IRにはベクトル関連命令が定義されてきているなど、ベクトル化技術を支える基盤技術が近年著しく進歩しています。今後の自動ベクトル化の発展に期待されるところです。

 

 

 

 

RISC-Vベクトル拡張命令の例(riscv-v-spec-japanesehttps://msyksphinz-self.github.io/riscv-v-spec-japanese/html/v-spec.html)から引用)