wiki:CALプログラミング(4)

Version 10 (modified by nakasato, 16 years ago) (diff)

--

N体計算

  • ここまでの説明を理解することで、最もシンプルなN体計算プログラムを作成できると思う。
  • 具体的にはGRAPEなどと同じように、ホストから粒子の位置と質量をGPUに送り、GPU上で全粒子間で互いに及ぼし合う力を足しあわせて、結果として加速度を得るようなプログラムである。粒子間に働く力がニュートン重力の場合は、CAL200808.pdf (60ページ以降)で説明しているIL kernelプログラムとなる。ループの部分のみを再掲すると、以下のようなものである。
     1  whileloop
     2    ige r88.x___, r100.x, r77.x
     3    break_logicalnz r88.x
     4    sample_resource(0)_sampler(0) r0, r2
     5    sub r5.xyz, r0.xyz, r4.xyz
     6    dp4 r6, r5, r5
     7    rsq r7, r6
     9    mul r8, r7, r7.xyz1
    10    mul r8, r8, r7.xyz1
    11    mul r9, r8, r5.xyz1
    12    mad r3, r9, r0.w, r3
    13    add r2.x___, r2.x, l1.x
    14    iadd r100.x___, r100.x, l0.z
    15    umod r101.x, r100.x, r77.y
    16    if_logicalz r101.x
    17      add r2.0y, r2.0y, l1.x
    18    endif
    19  endloop
    

  • データ構造の定義の詳細についてはファイルを参照のこと。
  • 2粒子間の重力の計算をおこなっている部分は5-12行である。
  • このILプログラムは、基本構造は(3)と同じであるが、ポインタ変数(ここでは"r2.xy")の処理が異なる。なぜかというと、粒子の座標が格納されているid = 0のリソースは2次元のメモリとして指定、確保されているからである。具体的には、ホスト側のプログラムで以下のようにメモリの確保をおこなった:
      calResAllocLocal2D(&inputRes, device, nx, ny,  CAL_FORMAT_FLOAT_4, 0);
    
  • こうする大きな理由は、"calResAllocLocal1D"により1次元のメモリとして確保した場合、その次元の最大値は8192までという制限があるためである。よって、上のILプログラムの場合には、1粒子のデータとして4要素のfloatと変数を使っているので、1次元メモリで単純にN体計算を実装すると8192粒子までの粒子しか扱うことができない。これでは実質的に利用価値がなく、実際にベンチマークテストをしてみると、この粒子数ではRV770の性能を引き出すことができない。

2次元配列

  • 2次元のメモリからデータを読み出すには、(3)では無視してきた読み込みポインタのy成分を適切にアップデートすればよい。上のILプログラムでは、13-18行の部分でその処理をおこなっている。
  • 繰り返しになるが、読み込みポインタとデータの対応関係は:
    v0.xy = {0.0, 0.0} ---> res0[0][0]
    v0.xy = {1.0, 0.0} ---> res0[0][1]
    v0.xy = {2.0, 0.0} ---> res0[0][2]
     ...
    v0.xy = {0.0, 1.0} ---> res0[1][0]
    v0.xy = {1.0, 1.0} ---> res0[1][1]
     ...
    
    のようになっている。
  • それを踏まえて13-18行をC言語に翻訳してみると、処理内容が理解できると思う:
      r2.x = r2.x + l1.x;
      r100.x = r100.x + l0.z;
      if (r100.x % r77.y == 0) {
        r2.x = 0.0;
        r2.y = r2.y + l1.x;
      }
    
  • 変数の意味と定数の値はそれぞれ以下のとおり:

"l1.x = 1.0", "l0.z = 1",