前提
CUDAを用いてMCTSにおけるシミュレーションの並列化処理を目指しています。MCTSを実装する対象としてBridgetというマイナーなボードゲームを用いています。
実現したいこと
- ゲームの合法手探索によって得られた合法手を格納する配列を長さ3000にて宣言したい。
- 宣言の際に、メモリの確保が過剰に行われているのを確認できたため、その過剰に確保される原因が知りたい。
発生している問題・エラーメッセージ
- 合法手を格納する配列laを宣言する際に確保されるメモリがGPUの持つメモリ量10018[MiB]より大きくなってしまうため、メモリ外アクセスのエラーを吐いてしまう。
- 配列laは大きさ3000のAction_cu型の配列であるため、Action_cuの大きさが56であることを考えると、単純計算で
3000 * 56 = 168000[B]
のメモリ使用で済むはずである。 - 実際に確保されているメモリ量が膨大になっていることが考えられる。
以下コンパイル後の実行にて得られた出力結果が以下になります。メモリ外アクセスを起こしているとのエラーメッセージを確認できます。
$ nvcc -O2 -lcurand -I./header -g -G -o obj/kernel.o -c src/kernel.cu $ nvcc -O2 -lcurand -I./header obj/state.o obj/action.o obj/piece.o obj/measuring.o obj/board.o obj/ucb1.o obj/node.o obj/game.o obj/mcts.o obj/cell.o obj/argmax.o obj/main.o obj/trans_data.o obj/kernel.o obj/playout_cuda.o -o bin/bridget $ ./bin/bridget Error: src/playout_cuda.cu:44, code:2, reason: out of memory
該当のソースコード
C++
1typedef struct _Piece_cu { 2 int kind; 3 int rdx; 4 int rdy; 5 6 int rcs[3][3]; 7} Piece_cu; 8 9typedef struct _Action_cu { 10 // どのような一手であるのかを示す構造体 11 // sizeof(Action_cu)にて大きさを測ったところ56であった。 12 int x, y; 13 14 Piece_cu piece; 15} Action_cu; 16 17__device__ int playout_gpu(State_cuda *sc, curandState *s) { 18 while (1) { 19 *** 割愛 *** 20 21 Action_cu la[3000]; // 合法手を格納したい配列を宣言している。 22 23 *** 割愛 **** 24 } 25}
試したこと
- 配列laのメモリ使用量が膨大であることが原因であるため、少しずつ大きくすることで実際にどれくらいの使用量となっているのか計測することが可能なのではないかと考えた。
Action_cu la[X]
のXを少しずつ大きくしていき、実際にメモリ使用量がどのように変化したのかnvidia-smi
を用いて計測した。
配列の大きさX | 1 | 10 | 100 | 1000 |
---|---|---|---|---|
メモリ使用量[MiB] | 339 | 389 | 893 | 5193 |
- これによって分かるのが、配列の大きさ1あたりで確保されるメモリサイズが5.6[MiB]となっていることである。
- 5.6[MiB]は本来の56[B]と比較すると約10^6倍もの差があることから明らかに不自然な挙動となっている。
バージョン等について
- OS: Ubuntu 20.04.2 LTS
- CUDA Version: 11.3
- GPU: GeForce RTX3080
最後に
こちらのサイトでの質問が初めてなことから書くべき情報が足りていないかもしれません。その際、必要な情報を追記させていただきますので、連絡いただけると幸いです。回答の程よろしくお願いします。
総スレッド数は幾つ?
1スレッドあたり158KBで、1e6個thraadを発生させれば158GB必要になる
返信ありがとうございます。
並列数のことでしたら、デバッグの段階ですので1並列にて実行しております。
playout_gpu<<<1,1,0,stream>>>(sc,s)ってこと?
dim3 block(threads_per_block, 1, 1);
dim3 grid((n_elem + block.x - 1) / block.x, 1, 1);
kernel<<<grid, block>>>(省略);
という形で実行しております。threads_per_block, n_elemともに1として実行しています。kernelはホスト側から最初に呼び出す関数として定義しており、そのkernel内からplayout_gpu関数を呼び出しております。
情報記載不足で申し訳ありません。
> 1並列にて実行
それを2, 4と増やしたら、配列の大きさ1あたりで確保されるメモリサイズも2倍、4倍になるのでしょうか?