サイト内の現在位置

ベクトルプロセッサ専用コンパイラオプション(その1)

no.0072021.1.29

 GNU Compiler CollectionのマニュアルのGCC Command Optionsには、C/C++、Objective-Cコンパイラのためのコンパイラオプションとして、文法診断メッセージ制御、デバッグ、最適化、リンクのためのオプションなど、多数のコンパイラオプションが記載されています。そこではx86プロセッサ専用など様々なプロセッサ向けコンパイラオプションも記載されており、中にはオルガン鍵盤のようなものがフロントに並ぶはるか昔のDEC PDP-11向けオプションまであり、GNU Compiler Collectionの長い歴史がうかがえます。

 一方、SX-Aurora TSUBASAのプロセッサはx86によるスカラ演算処理に加えて、NEC独自のベクトル処理機構を追加した独自アーキテクチャをもつプロセッサであることから、GNU Compiler Collectionのコンパイラを使った実行ファイルの作成はできません。その代わりに、NEC独自開発によるベクトルプロセッサ専用のコンパイラが用意されています。

 SX-Aurora TSUBASAに限らず、一般に多数のソースファイルに分割されたソフトウェアを開発する状況では、ソースファイルが増えるにつれてソースファイル間の依存関係が複雑化しがちです。そのため依存関係管理を容易にする方策として、makeコマンド・Makefileを使いファイルの依存情報の保持を行います。
 例えば、Makefileに複数のソースファイルでオブジェクトファイルを作る組み合わせや、ヘッダファイルとの組み合わせなどを記述します。その他、デバッグや最適化のためのコンパイラオプションである-gや-O、あるいはshared libraryを作成するよう指定する-fpicといったように、プログラムを作成するための様々な情報をMakefileの中に記述していきます。

 ベクトルプロセッサ専用コンパイラを使った実行ファイル作成においてもこの流れは変わりません。Makefileの中にソースファイル、リンクするライブラリ、使用するコンパイラ、そしてコンパイラオプション等を記述し、makeコマンドを実行する作業は全く同じです。Figure-1はnccコンパイラを使って3つのCソースファイルと2つのヘッダファイルをそれぞれコンパイルしてリンクするMakefileの一例です。
 CFLAGSに最適化レベルの指定やベクトル化オプションといったコンパイラオプションを指定し、コンパイルおよびリンク時にこれらを指定しています。なお、ここでコンパイラに指定したnccとはSX-Aurora TSUBASA専用Cコンパイラです。

Figure-1 nccコンパイラを指定したMakefileの一例

Figure-1 nccコンパイラを指定したMakefileの一例

 SX-Aurora TSUBASAのベクトルプロセッサで作動する実行ファイルを作成するためのベクトルプロセッサ専用コンパイラとして、Cコンパラであるncc、C++コンパイラとしてc++、FortranコンパイラnfortがNEC Software Development Kitに含まれています。これらncc、nc++、nfortのコンパイラオプションにはGNU Compiler Collectionと共通なものもありますが、さらにベクトルプロセッサに適したオブジェクトや実行ファイルを生成するために個別に用意されたコンパイラオプションも多数存在します。
 MakefileのCFLAGSに指定するオプション類はベクトルプロセッサ専用コンパイラがサポートするコンパイラオプションを指定してください。

 SX-Aurora TSUBASAの利用にあたり、正しいコンパイラオプションを選んで使うことは、高速な実行ファイルを作成する上で重要なポイントです。そこで今回は、これらベクトルプロセッサ専用コンパイラのコンパイラオプションの使い方について説明します。

 ベクトルプロセッサ専用コンパイラオプションは以下のカテゴリーに分類されます。
・コンパイラシステム全体を制御する全体オプション
・最適化・自動ベクトル化を制御する最適化・ベクトル化オプション
・自動並列化機能を制御する並列化オプション
・インライン展開機能を制御するインライン展開オプション
・性能測定やスタック領域の初期化を行う付加的なコードを生成するコード生成オプション
・デバッグをサポートするオブジェクトファイルを生成するデバッグオプション
・コンパイルメッセージやコンパイルリストの出力を制御するメッセージオプション
・リスト出力オプション
・プリプロセスを制御するプリプロセッサオプション
などです。

 これらの中でまず初めに、SX-Aurora TSUBASAでベクトル処理、並列処理でカギとなる最適化・自動ベクトル化オプションや並列化オプション、そしてインライン展開オプションについてCコンパイラでの例を使ってご紹介します。続いて、ベクトル化の程度や処理速度向上を確認するための性能測定やデバッグのためのコンパイラオプションについて使い方を見ていきます。


1.最適化・自動ベクトル化オプション
 GCCでは最適化レベルを指定するオプションとして-Oが用意されています。Cコンパイラであるncc(以降、nc++、nfortも同様)においても同様に最適化・自動ベクトル化レベルを指定する-Oオプションをサポートしています。nccでは、最適化・自動ベクトル化レベルを0から4の範囲で指定します。各レベルにおいての主な最適化・自動ベクトル化の内容をTable-1にまとめました。

 最適化・自動ベクトル化レベルを上げるに従って、コンパイラはより複雑なループ入れ換えや融合、演算順序の入れ換えといった最適化・自動ベクトル化を適用しベクトルプロセッサを効果的に使った演算を行えるオブジェクトファイルを生成します。

Table-1 主な最適化・自動ベクトル化とレベルの対応
Table-1 主な最適化・自動ベクトル化とレベルの対応

 また、最適化・自動ベクトル化レベルの概要は以下のとおりです。「副作用」については、後でご説明します。
 -O4:C/C++言語仕様を逸脱した副作用を伴う最大限の最適化・自動ベクトル化を適用する
 -O3:副作用を伴う最適化・自動ベクトル化、および、多重ループの最適化を適用する
 -O2:副作用を伴う最適化・自動ベクトル化を適用する(既定値)
 -O1:副作用を伴わない最適化・自動ベクトル化を適用する
 -O0:最適化、自動ベクトル化、並列化、インライン展開を適用しない

 -O0を指定したとき、コンパイラによる最適化、自動ベクトル化や自動並列化、自動インライン展開は行われません(ただし、NLCといったライブラリ関数をソースファイル内で呼び出す箇所では、ライブラリ自体がベクトル化処理されているため実行ファイルではその部分はベクトル化処理されます)。
 コンパイラによる最適化、自動ベクトル化が行われないため、ソースコードに記載された手順に従い処理が行われます。そのため、プログラムがベクトル化処理される比率は一般に低下します。

 Figure-2、3はそれぞれ最適化レベルを0、または2に設定してサンプルコードをコンパイルした際に出力されるコンパイルリスト(リスト出力オプション-report-allを指定しています)の一例です。最適化レベル0であるFigure-2の20行目から始まる多重ループ内の処理はベクトル化されていません。一方、レベル2のFigure-3では22行目の最内側ループ内の処理がベクトル化されると同時に、24行目の演算はベクトル積和演算(FMA)が適用されています。

 ここで、後に出てくるPROGINF機能を使って(Figure-5にPROGINF表示のための環境変数設定を示します)-O0、-O2で作成した実行ファイルを実行した時のベクト演算率を比較すると、-O0では平均ベクトル長22.00、ベクトル演算率0.01%であるのに対して、-O2の平均ベクトル長10.01、ベクトル演算率61.69%と、コンパイラが最適化・ベクトル化を行うことでベクトル演算が全体の処理に占める割合が大きく上がっています。

 実はこのコード、各ループの繰り返し数を指定するl,m,nは別のファイルにおいてグローバル変数として指定されています。この例ではl,m,nがそれぞれ10,20,250と定義されていますが、ループ内側ほどループの長さが短いため、レベル2により最内側ループiだけがベクトル化されても大きな効果は得られません。
 より大きなベクトル化効果を得るため、一つには3つのループを1つにまとめてループ長を長くするループ一重化(Loop Collapsing)という手法を使って人間がコードを書き換えるか(こうしたベクトル演算を意識したコーディング手法の紹介は別の機会に譲ります)、あるいは最適化レベルを2から3に上げてコンパイラの最適化機能を使った外側のループjをアンロールさせる、といった対処が考えられます。
 Figure-4がレベル3を指定した際に出力されるコンパイルリストです。Figure-6の各最適化レベルにおけるコンパイル・実行によるPROGINF出力に示したとおり、コンパイラ任せの最適化・ベクトル化だけでも実行時間は最適化無実施における12秒から-O2最適化後は0.64秒に、さらに-O3では0.43秒まで短縮され、大きな効果を得ることができています。

Figure-2 最適化レベル0でコンパイルでのリストファイル
Figure-2 最適化レベル0でコンパイルでのリストファイル
Figure-3 最適化レベル2でコンパイルでのリストファイル
Figure-3 最適化レベル2でコンパイルでのリストファイル
Figure-4 最適化レベル3でコンパイルでのリストファイル
Figure-4 最適化レベル3でコンパイルでのリストファイル
Figure-5 PROGINF表示のための環境変数設定
Figure-5 PROGINF表示のための環境変数設定
Figure-6 各最適化レベルでのコンパイル・実行によるPROGINFから抜粋
Figure-6 各最適化レベルでのコンパイル・実行によるPROGINFから抜粋

 一般的には、最適化レベルを上げていくにしたがいコンパイラがより複雑な最適化・ベクトル化を適用する結果、処理時間を短くできる可能性が大きくなります。

 ここでご注意いただきたいのが、レベル2以上では「副作用を伴う」と但し書きがつけられている点です。この副作用、例えば、最適化に伴い演算順序の変更や徐算の乗算化が行われることで演算結果が誤差範囲で異なるといったことが起きることがあります。
 また、Figure-3や4で示したようなベクトル積和演算(FMA)が使われると(何れもライン23に‘F’と表記があります)、途中の積算結果を丸めずに加算が行われることがあり、FMAを使わない演算結果と異なってしまう可能性がでてきます。その他にも、副作用を伴うベクトル化が行われる影響で、次のようなことが起きる可能性があります。
・式、コードの削除により、計算を行う場所、回数が変わり、エラーの発生位置、
  回数が最適化を適用しなかった場合と比べて変わることがあります。
・条件下の不変式のループ外への移動により、実行されないはずの式が実行され、
  本来発生しないはずのエラーや演算例外が発生することがあります。
・べき乗の最適化を適用したとき、アンダーフローが発生しても例外を検出しません。
・除算の乗算化により、演算結果にわずかの差異が生じます。浮動小数点数の演算では、
  この差異はほとんど無視してかまいませんが、無視したくないときコンパイラ
  オプションで最適化を抑止してください。
・命令の並べ換えによる最適化により、ある条件が成立したときのみ実行される計算が、
  基本ブロックをまたがって移動され、常に実行されるようになると、発生しないはずの
  実行時エラーが検出されることがあります。また、コンパイル時間やコンパイラが
  使用するメモリ量を著しく増加させることがあります。
・ループ内の最適配列データに対して演算処理を行う際に処理順序によっては参照する
  データが先に書き変えられてしまうような配列順序依存関係に伴う不正な処理が発生
  してしまう恐れがあります。このような依存関係が無いことに確信が持てないときには、
  -O3レベル以上の最適化・ベクトル化は選択しないことをお勧めします。

 最適化・自動ベクトル化オプションについての詳細はSX-Aurora TSUBASA C/C++コンパイラユーザーズガイドに記載された説明を確認の上、処理内容に応じてオプションを使い分けてください。

 先にも書きましたが、コーディングの段階でベクトル処理を意識したプログラムチューニングの技法が存在します。一方で、こうしたことをほとんど意識せずにコーディングしたコードであっても最適化レベルを適切に選びコンパイラの最適化・自動ベクトル化機能に頼りに実行ファイルにすることがあるレベルまで可能です。
 ループアンローリング、ループ融合などといった名前が付けられる技法を応用することは、SXシリーズの長い歴史を通して高いベクトル処理させるためのチューニング手法として受け継がれてきたものです。
 こうしたノウハウが詰め込まれたSX-Aurora TSUBASA専用コンパイラが最適化・自動ベクトル化をおこなうことで、高度なチューニング手法をマスターせずとも容易にベクトルプロセッサを活用していただけるようNECでは開発環境を提供しています。

次回に続く