CUDAでLambda関数

CUDAではCUDA 8からカーネル関数内でLambda関数が使えます. で,PTXコードがどうなるのか気になったので調べてみました.

検証コード

このコードには計3つのカーネル関数があります.
  1. kernel_0
    カーネル関数内に直にLambda関数を書くパターン
  2. kernel_1
    カーネル関数の引数にLambda関数を書くパターン
  3. kernel_1
    カーネル関数の引数にホストのデータをキャプチャするLambda関数を書くパターン

PTXコード

nvcc lambda.cu --ptx --expt-extended-lambda -arch=sm_75
でPTXコードを出力させます. マングリングされた
  1. _Z8kernel_0v
  2. _Z8kernel_1IZ4mainEUlvE_EvT_
  3. _Z8kernel_1IZ4mainEUlvE0_EvT_
の計3つのカーネル関数が見えます.
すべてinline展開されています.
このコードでのLambda関数の中身はコンパイル時には決定しているものなので普通の__device__関数と扱いは同じなようです.
特筆すべきは3つめのホストのデータをキャプチャするLambda関数でしょうか.
.visible .entry _Z8kernel_1IZ4mainEUlvE0_EvT_(
        .param .align 4 .b8 _Z8kernel_1IZ4mainEUlvE0_EvT__param_0[4]
)
...
ld.param.u32    %r1, [_Z8kernel_1IZ4mainEUlvE0_EvT__param_0];
...
st.local.u32    [%rd2], %r1;
...
とあるので,カーネル関数の引数で配列としてとしてキャプチャするデータが渡されているようです.

おまけ 0

カーネル関数内のprintfですが,出力する文字列は
.global .align 1 .b8 $str[7] = {112, 111, 105, 32, 48, 10, 0};
.global .align 1 .b8 $str1[7] = {112, 111, 105, 32, 49, 10, 0};
.global .align 1 .b8 $str2[10] = {112, 111, 105, 32, 50, 32, 37, 100, 10, 0};
のようにglobalな空間に定義しておいて,%dなどで変数を表示する場合は
st.local.u32    [%rd2], %r1;
のようにローカルメモリの__local_depot2に配置してvprintfを呼ぶんですね.

おまけ 1

C++のstd::functionのCUDA版,nvstd::functionがあるそうです.
詳しくはNew Compiler Features in CUDA 8をどうぞ.
余談ですが,NVIDIAはWMMA APIではnvcuda名前空間を使いこちらではnvstd名前空間をつかっているのでちょっぴり統一してほしいです.
カテゴリー:CUDA
記事作成日:2019-06-11