CUDAいじってて嵌ったのでメモ。
- // grid(1,1,1) thread(2,1,1) で実行、_buf[0]の初期値は1
- if(threadIdx.x==0){
- atomicAdd(&_buf[0],10);
- __syncthreads();
- mem0[0] = _buf[0];
- }else if(threadIdx.x==1){
- atomicAdd(&_buf[0],100);
- __syncthreads();
- mem1[0] = _buf[0];
- }
- return;
一見すると_buf[0]もmem0[0]もmem1[0]も111になりそうなコード。
でもmem1[0]は101になっちゃう。
何を間違えていたかというと、CUDAのthread内では条件分岐系が本当の分岐じゃないというか、マスクで潰されるだけで処理されるというか、そんな感じなのをちゃんと理解していなかったというお話。
今回の場合、ifの分岐は本当に分岐するわけではなく、マスクを生成して全スレッドが同じ処理をするわけだ。
コンパイル結果の都合だろうけど、今回はelse ifの方が先に実行されるというバイナリ(アセンブラ?)が出力されることになったらしく、
- atomicAdd(&_buf[0],100);
- __syncthreads();
- mem1[0] = _buf[0];
を各スレッドが実行。ただしthread0は演算結果を保持しない。この時点でmem1[0]=_buf[0]は101。
続いて
- atomicAdd(&_buf[0],10);
- __syncthreads();
- mem0[0] = _buf[0];
を各スレッドが実行。ただしthread1は演算結果を保持しない。この時点でmem0[0]=_buf[0]は111。
まぁこんな感じ。
なるほど納得。
まぁ今回の場合は
- if(threadIdx.x==0){
- atomicAdd(&_buf[0],10);
- }else if(threadIdx.x==1){
- atomicAdd(&_buf[0],100);
- }
- __syncthreads();
- if(threadIdx.x==0){
- mem0[0] = _buf[0];
- }else if(threadIdx.x==1){
- mem1[0] = _buf[0];
- }
- return;
って書けば良かったわけだな。
なんかソース内の空白がおかしくなった気がするけど、気にしない方向で。