本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

XLC で行こう!: 第 1 回 Cell/B.E.用 XLC は2種類あるよ

浅原 明広 (asahara@fixstars.com), 株式会社FIXSTARS 技術開発本部長 / AKD48
浅原 明広の肖像
浅原明広 (Akihiro Asahara, Ph.D.) は、2005 年の Cell/B.E. 発表初期の頃から、Cell/B.E. 関連のソフトウエア / ハードウエア開発業務に携わっているエンジニアです。学会発表や Cell/B.E. セミナー講師を多く努めているので、どこかで皆さんとお会いしたことがあるかもしれません。左の写真は、わかりにくいですが華麗にサーフィンするの図です。いや、Cell/B.E. とは特になんの関係もありません。

概要: XLC/C++ コンパイラは Power 系プロセッサに最適化されたコードを生成してくれる IBM 製の C/C++ 言語コンパイラです。Cell/B.E. アーキテクチャ用には、現在 2 種類の XLC/C++ コンパイラが用意されています。一つは標準ソフトウエア製品として販売されている "XL C/C++ Multi-core Acceleration for Linux version 9.0"、もう一つは Cell/B.E. SDK3.0 Extra Package にアルファ版として収録されている "XL C/C++ Alpha Edition Single Source Compiler version 0.9" です。この記事では、両コンパイラの違いについて説明し、効率よく開発を進める上でのヒントを紹介します。

日付:  2008年 7月 04日
レベル:  中級
アクティビティー: 5375 ビュー
お気軽にご意見・ご感想をお寄せください: 


XLC とは?

IBM XL C/C++ コンパイラ (以下 XLC と略す) は、Blue Gene 用や AIX 用など、システムアーキテクチャ毎にいくつかの種類が存在します。Cell/B.E. 対応版の XLC は、もともと Cell/B.E. SDK2.1 に収録されていたのですが、今回ベータ期間を終えて、めでたく XLC 製品群に統合されました。それが、"XL C/C++ Multicore Acceleration for Linux version 9.0" (以下、XLC MA と略す)です。

XLC MA は PPE 用と SPE 用の 2 つの異なるコンパイラから構成されていて、開発者は PPE 用の C/C++ コードと、SPE 用の C/C++ コードを作成し、対応するコンパイラでビルドすることになります。ビルド後は、PPE コードの中で SPE の実行イメージを Open してあげるか、SPE の実行形式ファイルをライブラリ化してリンクすることになります。この辺りの開発手順は、GCC などの GNU toolchain を使用して開発をした場合と同じですが、XLC の場合 PowerPC プロセッサ向けに最適化されたコードを生成してくれるため、GNU GCC などと比べ同じコードでも処理速度が改善される可能性があります。

一方、XLC MA とは別に、SDK3.0 Extra Package には "XL C/C++ Alpha Edition Single Source Compiler version 0.9" (以下、XLC SSC と略す) というパッケージが収録されています。このコンパイラはその名の通り、1 つのソースコードから、SPE, PPE, の両方を使用する実行形式ファイルを生成してくれるというもので、開発者がソースコードに #pragma 文として埋め込むことで SPE に処理させる部分をコンパイラに指定する OpenMP のプログラミングモデルに準拠しています。元々 OpenMP は、SMP(Shared memory Multi Processer) 構成のシステム向けに策定された並列化手法であり、MPI などと異なり、通常の逐次処理コードと、並列化されたコードを全く同一のコードで管理できるのが魅力です(あとで詳しく説明します)。


XLC MA 概説

まず最初に XLC MA について、説明していきます。XLC MA は製品なので使用には有償のライセンスが必要なのですが、60 日間の無料評価版をこちらの Web ページからダウンロードできます。

利用方法は GNU GCC コンパイラと全く変わりありませんので、Cell/B.E. ユーザーであれば単に Makefile の書き換えのみを行うだけで、XLC MA で最適化されたコードを得ることができます。以下では、XLC 特有の重要なコンパイルオプションについて説明します。

-qhot

まず一つ目の重要な最適化オプションは High Order Transformations オプション、-qhot です。qhot はループ箇所のハイレベルな最適化を行うオプションであり、書式は次のようになります。

-qhot[=[no]vector|arraypad[=n]|[no]simd]

重要な引数の意味は次の通りです。

vector | novector
ループ内の演算を MASS ライブラリ(数学関数ライブラリ) の呼び出しに変換します。ただし、丸め誤差の違いで標準数学関数の場合と結果が異なる可能性があるので注意が必要です。novector を引数で与えると、コードの MASS ライブラリコールへの置き換えを禁止します。

simd | nosimd
ループ内の演算で可能な部分をベクトル演算命令に変換します。PPEコードの場合は VMX 命令が、SPE コードの場合は SPU SIMD 命令が呼ばれることになります。nosimd を引数で与えると、ベクトル演算命令への置き換えを禁止します。

arraypad | noarraypad
PowerPC プロセッサのキャッシュアーキテクチャでは、2 の累乗の次元を持つ配列についてキャッシュの使用効率が落ちることがあります。このオプションではそのような効率低下を防ぐためにコンパイラが配列の次元を増やします。
arraypad オプションを単独で使った場合は、キャッシュ使用効率があがる可能性のある配列をコンパイラが自動で選択し、次元の増加を行います。arraypad = n (n は整数)などと arraypad 数値を与えることもできます。この場合コンパイラはコード内部の全ての配列に対して、あたえられた整数n を要素数とした配列の拡張 (Padding) を行います。整数値はソースコードの配列の中で最も大きな要素サイズの倍数 (たとえば、4、8、16など) に設定するとよいでしょう。
noarraypad を引数であたえると、上記のようなPaddingを行いません。

level=0
-qhot=novector:nosimd:noarraypad

と同じです。

level=1
  • -qhot
  • -qhot=vector:simd
と同じです。

qhotオプションのPragma制御

ソースコード中に #pragma nosimd, #pragma novector 文を埋め込むことで、特定のループ毎に -qhot=vector-qhot=simd の効果を消すことができます。例を見たほうが理解が早いでしょう。以下の例では、SIMD 化したくないループブロックに関して pramga を使っています。

...

#pragma nosimd
for(i=1;i<1000;i++){
	a[i] = b[i] * c[i]
}

...

-qtune, -qarch

特定のアーキテクチャーに依存したコードを生成し、高速化します。-qarch は命令の生成対象となるアーキテクチャーを指定し、-qtune はコードの最適化対象とするターゲット・プラットフォームを指定する、ということになっていて、-qarch を指定していない時の-qtune の動作や、-qtune を指定していない時の -qarch の動作など、いろいろ規定はあるのですが、個々のケースに対応するのは大変なので、私はいつもアーキテクチャ依存最適化を行う際は両方指定しています。指定できるアーキテクチャは、XLC MA の場合、

  • -qarch=auto -qtune=auto (ターゲットを自動判別)
  • -qarch=cellppu -qtune=cellppu (PPUをターゲットとしてコンパイル)
  • -qarch=cellspu -qtune=cellspu (SPUをターゲットとしてコンパイル)
  • -qarch=edp -qtune=edp (PowerXCell SPU をターゲットとしてコンパイル)

の 4 種類です。このうち、auto は、現在コンパイルしている環境が実行環境と思ってアーキテクチャを自動で決定してくれます。そのため、x86 系システムの上で Cell/B.E. の開発を行うようなクロスコンパイル環境で開発をする場合は注意して下さい。明示的にアーキテクチャ名を指定する方がよいでしょう。

-O

GNU GCC コンパイラでもおなじみの最適化オプションです。XLC MA では、-qnoopt, -O2, -O3, -O4, -O5, の 5 段階の最適化レベルを持っています。段階があがるにつれて、生成されるコードの高速化が期待できますが、コンパイルにかかる時間、メモリ使用量、生成されたコードのサイズ、が大きくなる傾向にあります。また、全く最適化を行わない場合とくらべ、高いレベルの最適化では計算順序の入れ替えなどにより、浮動小数点演算の結果が多少変わる可能性があります。

例えば、-O3 では、効率が上がる場合は a*b*ca*c*b に置き換えます。また、以下の例では、b+c の演算結果はループの間中変化しないので、ループの前に一度だけ演算を行い、適当なレジスタにその結果を保持しておけば高速化ができます。-O3 ではそのような最適化がコンパイラによって行われますが、-O2 では、1. 浮動小数点演算であるため、コードの変更が危険とみなされる (浮動小数点例外などが発生する可能性があるので)。2. ループの全てのパスで発生するわけではない。という理由で、演算がループの外に出されません。

...

int i;
float a[100], b, c;
for (i = 0 ; i < 100 ; i++){
	if (a[i] < a[i+1])
		a[i] = b + c;
	}
 ...

お勧めの最適化オプションの使い方

実は、-O4 の最適化は、-O3 -qarch=auto -qtune=auto -qcache -qhot -qipa と同じです。また、-O5 の最適化は、-O4 -qipa=level2 と同じです。-O4, -O5 のオプションは、計算結果がかなり変わってしまうことがあるのと、qarch が auto 設定になっているため、クロスコンパイル環境ではよくない結果になることがあります。よって、お勧めの最適化手順としては、

  1. 最適化オプション無しで十分にデバックする
  2. -O3 オプションを試してみる
  3. -qhot, -qarch -qtune, -qcache などを一つづつ試していく

という流れがよいと思います。


XLC SSC 概説

次はお待ちかねの Single Source Compiler について説明します。XLC SSCはOpen MP API V2.5 をサポートしたコンパイラであり、#pragma 文により生成されたスレッドは、自動的に各 SPE に分配されて処理されます。コンパイラがこのような 2 つの異なるアーキテクチャコードをどのようにして取り扱っているのか興味のある方は、こちらの技術論文を参照するとよいでしょう。本記事では、あくまでも開発者が"ツールとしてこの XLC SSC が使える"かどうか、調査していくという立場を取ります。

XLC SSC のインストール

XLC SSC は Cell/B.E. SDK 3.0 Extra Package に含まれていますが、デフォルトではインストールされないので、使用する際は別途手動でインストールする必要があります。パッケージは以下の表にあげた 7 種類になります。また、開発環境が、Power 系か、Intel x86 系かで用意されているパッケージが異なるので注意して下さい。

パッケージの種類IBM POWER サポートIntel x86 サポート
C/C++ runtime
(再配布可能)
cell-xlc-ssc-rte-0.9.0-f.ppc64.rpm cell-xlc-ssc-rte-0.9.0-f.i386.rpm
C/C++ runtime links cell-xlc-ssc-rte-lnk-0.9.0-f.ppc64.rpm cell-xlc-ssc-rte-lnk-0.9.0-f.i386.rpm
C/C++ libraries cell-xlc-ssc-lib-0.9.0-f.ppc64.rpm cell-xlc-ssc-lib-0.9.0-f.i386.rpm
C/C++ OMP libraries cell-xlc-ssc-omp-0.9.0-f.ppc64.rpm cell-xlc-ssc-omp-0.9.0-f.i386.rpm
C/C++ compiler cell-xlc-ssc-cmp-0.9.0-f.ppc64.rpm cell-xlc-ssc-cmp-0.9.0-f.i386.rpm
C/C++ help and documentation cell-xlc-ssc-help-0.9.0-f.ppc64.rpm cell-xlc-ssc-help-0.9.0-f.i386.rpm
C/C++ manpages cell-xlc-ssc-man-0.9.0-f.ppc64.rpm cell-xlc-ssc-man-0.9.0-f.i386.rpm

XLC SSC の実行

XLC SSC の実行コマンドは以下の 3 つです。

  • cbexlc
  • cbexlc++
  • cvexlC

このうち、cbexlc++, cbexlC は、C ソース、C++ ソース、どちらもコンパイル出来ますが、cbexlc では C++ ソースはコンパイルできません。

コンパイルオプションは基本的には XLC MA と同じです。ただし、一度のコンパイルで PPE, SPE, 2 つのアーキテクチャの命令を生成するため、いくつかのオプションが追加されています。以下に代表的なものを挙げておきます。

-qarch=cell -qtune=cell
qarch, qtune オプションは特定のアーキテクチャ依存のチューニングを行うものですが、XLC SSC では PPE, SPE, 2 つのアーキテクチャのチューニングを行うための、"cell" というアーキテクチャ名をオプションに与えることができます。

-qarch=celledp -qtune=celledp
同様に、IBM PowerXcell 8i プロセッサの PPE, SPE, 2 つのアーキテクチャのチューニングを行うためのオプションです。

-qnosmp
XLC SSC にはデフォルトで -qsmp オプションがついていて、OpenMP による並列化が可能ですが、明示的に OpenMP pragma を無視したい場合に、-qnosmp を使います。

サンプルコード (Euler 粒子シミュレーション)

OpenMP 自体の説明は次回の記事で詳しく行うとして、まずは XLC SSC の威力をサンプルコードで検証してみましょう。"Software Development Kit for Multicore Acceleration Version3.0 Programming Tutorial" に掲載されている Euler Particle Simulation のソースコードを題材として使ってみることにします。


リスト 1. Euler 法のサンプルプログラム (OpenMP による並列化)
                
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <omp.h>
#include "euler.h"

struct  timeval tv1, tv2;
int elapsed_microsecs;
float elapsed_time;

vec4D pos[PARTICLES];		// particle positions
vec4D vel[PARTICLES];		// particle velocities
vec4D force;			// current force being applied to the particles
float inv_mass[PARTICLES];	// inverse mass of the particles
float dt = 0.001f;	        // step in time

int main(int argc, char *argv[])
{
  int i;
  int m;
  float time;
  float dt_inv_mass;
  double result;
  float xpos;
  float ypos;
  int nthreads;

  if(argc!=2){
    printf("usage: >euler <thread num>\n");
    return 1;
  }

  //initialize
  force.x = 0.0;
  force.y = 0.0;
  force.z = -9.80;  // [m/s]:gravity constant

  srand(111);

  for (i=0; i<PARTICLES; i++) {
    pos[i].x = 0.0;
    pos[i].y = 0.0;
    pos[i].z = 0.0;

    vel[i].x = ((float)rand()/(RAND_MAX + 1.0)*2.0 - 1.0);
    vel[i].y = ((float)rand()/(RAND_MAX + 1.0)*2.0 - 1.0);
    vel[i].z = ((float)rand()/(RAND_MAX + 1.0)*2.0 - 1.0);// [m/s]

    inv_mass[i] = 0.001; // [kg]
  }//line 50

  nthreads = atoi(argv[1]);
  omp_set_num_threads(nthreads);

  gettimeofday(&tv1,NULL);

#pragma omp parallel default(none) private(i,time,dt_inv_mass)	\
  shared(pos,vel,dt,force,inv_mass,nthreads)
  {
    nthreads = omp_get_num_threads();
#pragma omp for
    // For each particle
    for (i=0; i<PARTICLES; i++) {
      
      // For each step in time
      for(time=0.0;time<END_OF_TIME;time += dt){
	pos[i].x = vel[i].x * dt + pos[i].x;
	pos[i].y = vel[i].y * dt + pos[i].y;
	pos[i].z = vel[i].z * dt + pos[i].z;

	dt_inv_mass = dt * inv_mass[i];

	vel[i].x = dt_inv_mass * force.x + vel[i].x;
	vel[i].y = dt_inv_mass * force.y + vel[i].y;
	vel[i].z = dt_inv_mass * force.z + vel[i].z;
      }
    }
  }
  gettimeofday(&tv2,NULL);
  elapsed_microsecs =
    (int)(tv2.tv_sec  - tv1.tv_sec) * 1000000 +
    (int)(tv2.tv_usec - tv1.tv_usec);
  elapsed_time = (float) elapsed_microsecs * 0.000001;

  printf("Nthread = %d, Elapsed time = %f \n",nthreads,elapsed_time);
  return (0);
}

Software Development Kit for Multicore Acceleration Version3.0 Programming Tutorial" をみていただくとわかりますが、リスト 1. は Euler 法とよばれる粒子シミュレーションアルゴリズムをそのまま抜き出したものです。ただし Tutorial のものと異なり、

  1. 粒子の初期値を定めています
  2. 引数にスレッドの数を与えられるようにしています
  3. ループの前後で gettimeofday 関数で経過時間の測定を行っています

XLC SSC のための並列化特有の部分は 51 行目以下で、51 行目の関数 omp_set_num_threads(nthreads) によって、OpenMP によって並列化されるスレッドの個数を指定しています。この関数は、omp.h をインクルードして使用します。57 行目で OpenMP に規定された pragma 文により、並列化部分の変数の種類を定義し、61 行目でループブロックの並列化をしています。

もし、リスト 1 のコードを OpenMP に対応していないコンパイラでビルドした場合、#pragma 文は単に無視されるだけです。よって、通常の逐次処理のコードも、並列化されたマルチスレッドのコードも、ひとつのソースコードで管理できるということになります。これは、OpenMP の大きな特徴の一つです。

さて、ざっと見てきましたがどうでしょうか?簡単なプログラムなので、違和感はないと思います。Euler 法のさらに詳しい説明は Software Development Kit for Multicore Acceleration Version3.0 Programming Tutorial" を参照下さい。また、OpenMP の詳しい説明は次回の記事で詳しく説明します。いまのところは、そういうものだと思って、先に進みましょう。

リスト 1 でインクルードしている euler.h は以下のようになります。


リスト 2. Euler 法のヘッダープログラム
                
#ifndef _EULER_H_
#define _EULER_H_

#define END_OF_TIME	300
#define PARTICLES	10000

/* 4-D vector definition. */
typedef struct {
  float x, y, z, w;  
} vec4D;

#endif /* _EULER_H_ */

本記事での実験では END_OF_TIME の値を 300 秒に、また、PARTICLES の値を 10000 個にしています。時間や粒子の数を適切にして、タイムステップ毎に粒子の場所をファイルに出力し、gnuplot などの可視化ツールを使用すれば、図 1. のようなきれいな粒子の飛跡を得ることができます。もちろん、表示系を自分で工夫すれば、シミュレーションが進むにつれて飛跡が伸びていくようなリアルタイム表示のアプリケーションにすることも可能です。


図 1. Euler 法シミュレーションの可視化例 (100particle, 300sec)

Makefile は以下のようになります。


リスト 3. Euler 法の Makefile
                
CC=/opt/ibmcmp/xlc/ssc/0.9/bin/cbexlc
CFLAGS=-O3 -qarch=cell -qtune=cell

all: euler

euler: euler.c
	$(CC) $(CFLAGS) -o $@ $(INCLUDE) euler.c $(CLIB)
clean:
	rm -f euler *.o *.d

2 行目をみればわかるように、最適化オプションとして -O3 と、アーキテクチャ依存最適化の指定を行っています。

測定結果

上記のプログラムをXLC SSCでコンパイルし、さっそく速度計測をしてみましょう。ここでは、IBM BladeCenter QS20 (Cell/B.E.プロセッサ3.2GHz, 1GB memory)で速度計測をしてみることにします。図 2. がその結果です。横軸のスレッドの数に応じて処理にかかった経過時間が減少しているのがよくわかります。スレッド数が17のときに、経過時間は最小になり、それ以上の数を引数に与えてもomp_get_thread_numで返されるスレッド生成数が増えないことがわかると思います。この17という数は、SPE x 8 + QS20上のもう一つのCell/B.E.プロセッサのSPE x 8 + メインスレッドのPPE = 17 だと考えられます。


図 2. Euler 法プログラムの測定結果


まとめ

XLC MA では、重要ななコンパイルオプションについて、その概要を説明しました。Cell/B.E. ソフトウエア開発では、GNU GCC だけではなく、XLC MA も是非一度試して見て下さい。労せずしてコードの高速化に成功するかもしれません。

XLC SSC では、OpenMP によるお手軽な並列化手法を、Euler 法というプログラムを題材にして見てみました。ほんの少しの修正で、スカラーコード (PPE1 コアによるシングルスレッドコード) にくらべ、17 コアを使ってちょうど 17 倍の速度向上を得ることができました。Dual Source コンパイラでカリカリにチューニングした場合はもっとよい数字が得られるかもしれませんが、17 倍というのは、かかった労力 (たった数行の追加!)を考えれば悪くない数字です。

次回「XLC で行こう! 第 2 回」では、OpenMP のプログラミング記法についてさらに詳しく説明していく予定です。


参考文献

学ぶために

製品や技術を入手するために

議論するために

著者について

浅原 明広の肖像

浅原明広 (Akihiro Asahara, Ph.D.) は、2005 年の Cell/B.E. 発表初期の頃から、Cell/B.E. 関連のソフトウエア / ハードウエア開発業務に携わっているエンジニアです。学会発表や Cell/B.E. セミナー講師を多く努めているので、どこかで皆さんとお会いしたことがあるかもしれません。左の写真は、わかりにくいですが華麗にサーフィンするの図です。いや、Cell/B.E. とは特になんの関係もありません。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Multicore acceleration, Linux
ArticleID=317770
ArticleTitle=XLC で行こう!: 第 1 回 Cell/B.E.用 XLC は2種類あるよ
publish-date=07042008
author1-email=asahara@fixstars.com
author1-email-cc=

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。