Changes between Version 18 and Version 19 of CALプログラミング(4)
- Timestamp:
- Mar 22, 2009 1:15:32 PM (16 years ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
CALプログラミング(4)
v18 v19 3 3 4 4 = N体計算 = 5 *ここまでの説明を理解することで、最もシンプルなN体計算プログラムを作成できると思う。5 ここまでの説明を理解することで、最もシンプルなN体計算プログラムを作成できると思う。 6 6 7 *具体的にはGRAPEなどと同じように、ホストから粒子の位置と質量をGPUに送り、GPU上で全粒子間で互いに及ぼし合う力を足しあわせて、結果として加速度を得るようなプログラムである。粒子間に働く力がニュートン重力の場合は、[http://galaxy.u-aizu.ac.jp/trac/note/attachment/wiki/CALによるGPUプログラミング/CAL200808.pdf CAL200808.pdf] (60ページ以降)で説明しているIL kernelプログラムとなる。ループの部分のみを再掲すると、以下のようなものである。7 具体的にはGRAPEなどと同じように、ホストから粒子の位置と質量をGPUに送り、GPU上で全粒子間で互いに及ぼし合う力を足しあわせて、結果として加速度を得るようなプログラムである。粒子間に働く力がニュートン重力の場合は、[http://galaxy.u-aizu.ac.jp/trac/note/attachment/wiki/CALによるGPUプログラミング/CAL200808.pdf CAL200808.pdf] (60ページ以降)で説明しているIL kernelプログラムとなる。ループの部分のみを再掲すると、以下のようなものである。 8 8 {{{ 9 9 1 whileloop … … 27 27 }}} 28 28 29 *データ構造の定義の詳細についてはファイルを参照のこと。29 2粒子間の重力の計算をおこなっている部分は5-12行である。データ構造の定義の詳細についてはファイルを参照のこと。 30 30 31 * 2粒子間の重力の計算をおこなっている部分は5-12行である。 32 33 * このILプログラムは、基本構造は(3)と同じであるが、ポインタ変数(ここでは"!r2.xy")の処理が異なる。なぜかというと、粒子の座標が格納されているid = 0のリソースは2次元のメモリとして指定、確保されているからである。具体的には、ホスト側のプログラムで以下のようにメモリの確保をおこなった: 31 このILプログラムは、基本構造は(3)と同じであるが、ポインタ変数(ここでは!r2.xy)の処理が異なる。なぜかというと、粒子の座標が格納されているid = 0のリソースは2次元のメモリとして指定、確保されているからである。具体的には、ホスト側のプログラムで以下のようにメモリの確保をおこなった: 34 32 {{{ 35 33 calResAllocLocal2D(&inputRes, device, nx, ny, CAL_FORMAT_FLOAT_4, 0); 36 34 }}} 37 35 38 * こうする大きな理由は、"calResAllocLocal1D"により1次元のメモリとして確保した場合、その次元の最大値は8192までという制限があるためである。よって、上のILプログラムの場合には、1粒子のデータとして4要素のfloatと変数を使っているので、1次元メモリで単純にN体計算を実装すると8192粒子までの粒子しか扱うことができない。これでは実質的に利用価値がなく、実際にベンチマークテストをしてみると、この粒子数ではRV770の性能を引き出すことができない。36 こうする理由は、"calResAllocLocal1D"により1次元のメモリとして確保した場合、その次元の最大値は8192までという制限があるためである。よって、上のILプログラムの場合には、1粒子のデータとして4要素のfloatと変数を使っているので、1次元メモリで単純にN体計算を実装すると8192粒子までの粒子しか扱うことができない。これでは実用的な価値がなく、実際にベンチマークテストをしてみると、この粒子数ではRV770の性能を引き出すことができない。 39 37 40 38 = 2次元配列 = 41 *2次元のメモリからデータを読み出すには、(3)では無視してきた読み込みポインタのy成分を適切にアップデートすればよい。上のILプログラムでは、13-18行の部分でその処理をおこなっている。39 2次元のメモリからデータを読み出すには、(3)では無視してきた読み込みポインタのy成分を適切にアップデートすればよい。上のILプログラムでは、13-18行の部分でその処理をおこなっている。 42 40 43 *繰り返しになるが、読み込みポインタとデータの対応関係は:41 繰り返しになるが、読み込みポインタとデータの対応関係は: 44 42 {{{ 45 43 v0.xy = {0.0, 0.0} ---> res0[0][0] … … 49 47 v0.xy = {0.0, 1.0} ---> res0[1][0] 50 48 v0.xy = {1.0, 1.0} ---> res0[1][1] 49 v0.xy = {2.0, 1.0} ---> res0[2][1] 51 50 ... 52 51 }}} 53 52 のようになっている。 54 53 55 *それを踏まえて13-18行をC言語に翻訳してみると、処理内容が理解できると思う:54 それを踏まえて13-18行をC言語に翻訳してみると、処理内容が理解できると思う: 56 55 {{{ 57 56 r2.x = r2.x + l1.x; … … 63 62 }}} 64 63 65 *変数の意味と定数の値はそれぞれ以下のとおり:64 変数の意味と定数の値はそれぞれ以下のとおり: 66 65 ||!r2.xy || 配列読み込みのポインタ || 67 66 ||!l1.x || 浮動小数点定数 1.0 || … … 69 68 ||!l0.z || 整数定数 1 || 70 69 ||!r77.y || 整数定数 配列の最下位次元の大きさ 256 || 71 "!r77.y"の意味について補足すると、256になるのは、id = 0のメモリが"res0![256]![256]"として宣言されているからである。72 70 73 * よってILプログラムのこの部分は、2次元配列のポインタ演算をおこなっているのと同等である。細かく説明すると、!r2.x(1次元目の添え字に対応)が0から1ずつ増えていき、256に等しくなったたら、それを0にクリアし、!r2.y(2次元目の添え字に対応)をインクリメントしている。 71 !r77.yの意味について補足すると、256になるのは、id = 0のメモリが"res0![256]![256]"として宣言されているからである。 72 73 よってILプログラムのこの部分は、2次元配列のポインタ演算をおこなっているのと同等である。細かく説明すると、!r2.x(1次元目の添え字に対応)が0から1ずつ増えていき、256に等しくなったたら、それを0にクリアし、!r2.y(2次元目の添え字に対応)をインクリメントしている。 74 74 75 *この13-18行を基にすることで、任意のサイズの2次元配列のランダムアクセスが可能であることがわかると思う。このILプログラムの場合、アクセスパターンが一定でありバースト的なため、GPUの内部構造に(特にキャッシュ機構)に適しており、非常に高速に動作する。一方、もしランダムアクセスを実装すると、メモリ読み込みのレイテンシーがパフォーマンスの問題となる可能性がある。75 この13-18行を基にすることで、任意のサイズの2次元配列のランダムアクセスが可能であることがわかると思う。このILプログラムの場合、アクセスパターンが一定でありバースト的なため、GPUの内部構造に(特にキャッシュ機構)に適しており、非常に高速に動作する。一方、もしランダムアクセスを実装すると、メモリ読み込みのレイテンシーがパフォーマンスの問題となる可能性がある。 76 76 77 *"calResAllocLocal2D"または"calResAllocRemote2D"で確保した場合でも、それぞれの次元でのサイズの最大値は8192と思われる。よって、ひとつのメモリ領域に割り当て可能な最大メモリ量は、256M個のfloat変数(またはLocalの場合、ボードの搭載メモリ量の制限値まで)になる。(この項詳しく調べる必要あり)77 "calResAllocLocal2D"または"calResAllocRemote2D"で確保した場合でも、それぞれの次元でのサイズの最大値は8192と思われる。よって、ひとつのメモリ領域に割り当て可能な最大メモリ量は、256M個のfloat変数(またはLocalの場合、ボードの搭載メモリ量の制限値まで)になる。(この項詳しく調べる必要あり) 78 78 79