CUDA BY EXAMPLE、とりあえずひと通りやってみた。 ただし OpenGL/GLUT は動かないので8章は飛ばした。 あと、使っている MacBook Air にはデバイスが一つしか載ってないから複数デバイスのところは読むだけ。
ストリームとワープのあたりがまだ理解できた気がしないので、さらに何かを読まなければいけなそうだ。
CUDA BY EXAMPLE、とりあえずひと通りやってみた。 ただし OpenGL/GLUT は動かないので8章は飛ばした。 あと、使っている MacBook Air にはデバイスが一つしか載ってないから複数デバイスのところは読むだけ。
ストリームとワープのあたりがまだ理解できた気がしないので、さらに何かを読まなければいけなそうだ。
8章のサンプルコード、どれも問題なくコンパイルできるが、すべて Bus error で落ちる。Crash reporter のログによると、glGenBuffers あたりで落ちているように見えるが…MacPorts で入れた mesa を使っているからよくないのだろうか。
OpenGL/GLUT はよく分からないので、とりあえず8章は飛ばすことにしよう。
108ページの tex1Dfetch() の説明がちょっと分かりにくい。次のように解釈した:
tex1Dfetch()は関数のように見えるが、実は関数ではなく、コンパイラに組み込まれている機能である。その実装では、tex1Dfetch()でアクセスするテクスチャが(リンク時や実行時でなく)コンパイル時に判明している―テクスチャ参照の宣言がコンパイラに見えている―必要がある。このため、tex1Dfetch()を用いてテクスチャにアクセスするblend_kernel()に例えば引数としてテクスチャ参照へのポインタを渡すことで入力バッファと出力バッファを切り替えることはできない。そこで今回のコードでは、テクスチャ参照2つをファイルスコープで(=関数の外で)宣言し、どちらのバッファを使うかをフラグdstOutで指定することにした。
ただ、テクスチャ参照は左辺値のようだし(sizeof できるしポインタも取れる)、tex1Dfetch() も関数のようだ(ポインタを取ろうとすると overloaded function と言われる)。本の nvcc と自分が使っている nvcc で版が違うのかも知れない。
$ nvcc -V nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2010 NVIDIA Corporation Built on Thu_Nov_11_15:26:50_PST_2010 Cuda compilation tools, release 3.2, V0.2.1221
anim_gpu() 内で dstOut を volatile としてる理由は謎。
あと、試しにテクスチャ参照をブロック内で宣言してみたら、どうも勝手に static 扱いになってるみたいだった。
CUDA BY EXAMPLE 5.3.1節。 N次元ベクトル2つの内積を、256スレッドからなる複数のブロックを使って求める。 ブロック数は32個とするが、Nが小さい場合には必要な数だけのブロックを起動する。 各ブロックは、担当の部分の要素の積の和を求めて結果をCPUに返す。 よって32個以下の値がCPUに返ってくる。この総和(が内積である)をCPUは求めて表示する。
各スレッドの仕事は以下のようになっている。
Nがぴったり256の倍数でない場合の処理は常套句の while (tid < N) で行われている。
各スレッドが自分の担当するところの積の和を求めたら、ブロック内で縮約して総和を求め、CPUに返す(カーネルの仮引数c、mainから見ると(dev_)partial_c)。CPUは返された32個(あるいはそれ以下)の値の総和を求める。
68ページにある最小倍数
回りの分かりにくい部分は、こう読むといい:「データ要素がN個なので、ドット積を計算するのに必要なスレッドはN個である。スレッド数が256個*32ブロックでは多すぎる場合には、threadsPerBlock (=256) の倍数であってかつN以上であるような最も小さい個数のブロックを用いればよい。これはベクトルの和でも取り上げた。今回の場合は、(N+threadsPerBlock-1))/threadsPerBlock を計算することで、そのようなブロック数を求める。」
ビジー状態を保つ
はよく分からない。
「CUDA BY EXAMPLE」の5.2.1節にある「長いベクトルの和」の項が分かりにくかったので覚えとして。
51ページ:
この変数はすべてのブロックに対する定数であり、ブロックの各次元のスレッド数を含んでいます。
原文にあたってないけど、この部分は意味は多分:
「この変数は全てのブロックについて同じ(一定=constant)であり、ブロックを構成する3次元に並んだスレッドたちの各次元のスレッド数をメンバとして持っています。」
まとめて書かれていないようだけど、どうやらグリッドとブロックとスレッドの関係はこんな感じらしい:
gridDim(41-42ページ)は、その各次元(x方向およびy方向)のブロック数を持っている。gridDim.x がx方向のブロック数、gridDim.y がy方向のブロック数である。blockDim は、その各次元(x, y, z方向)のスレッド数を持っている。blockDim.x がx方向のスレッド数、などとなる。
ついでに小さいところ。
50ページ:複数のブロックとスレッドを使う場合、インデックスを計算する方法は、2次元のインデックス空間を線形空間に変換するための標準的な手法に似てきます。
「線形空間」は「1次元 (linear) 空間」と読んだほうが分かりやすい。
CUDA BY EXAMPLE 4.2.2節でジュリア集合を計算する例が出てくる。GLUT を入れてなかったので少し手間だった。以下、作業内容。
export DYLD_LIBRARY_PATH=/usr/local/cuda/lib。
GLUT のヘッダファイルとライブラリが nvcc から見えるように、コンパイルはこう↓する。
nvcc -I/opt/local/include -m 64 -o julia_cpu julia_cpu.cu -L/opt/local/lib -lglut -lGL
MacPorts で入れた libglut などが64ビットだったので -m 64 が必要になった。
これで動いた。終了するには画像ウィンドウの方で Esc を押す。
julia_gpu の方も同じやり方で動いた。4章終わり。
追記:Mac OS X には標準で OpenGL や GLUT が入っているようだけど、それを使うような設定が分からなかったので結局 mesa にした。うまくやればできそう。参考:GLUTによる「手抜き」OpenGL入門
和訳の方。1章2章はさくさく読み進めて3章。
../common/book.h は SDK ではなくこの本のサイトで提供されているファイル(.zip に入ってる)だった。.zip ファイルを展開して chapter03/ の下に行き、nvcc hello_world.cu したら a.out ができて、ちゃんと動いた。
楽するために簡単な Makefile を作った。
.SUFFIXES: .cu .cu: <タブ>nvcc -o $@ $<
これで make hello_world とかするとコンパイルできる。