Mais conteúdo relacionado
Semelhante a CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説 (20)
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
- 8. PTX(関数全体)
ver.1.2 8
01 .version 6.0
02 .target sm_60
03 .address_size 64
04
05 // .globl _Z4axpyPd
06
07 .visible .entry _Z4axpyPd(
08 .param .u64 _Z4axpyPd_param_0
08 )
09 {
10 .reg .b32 %r<2>;
11 .reg .f64 %fd<3>;
12 .reg .b64 %rd<5>;
13
14
15 ld.param.u64 %rd1, [_Z4axpyPd_param_0];
16 cvta.to.global.u64 %rd2, %rd1;
17 mov.u32 %r1, %tid.x;
18 mul.wide.s32 %rd3, %r1, 8;
19 add.s64 %rd4, %rd2, %rd3;
20 ld.global.f64 %fd1, [%rd4];
21 add.f64 %fd2, %fd1, 0d4020000000000000;
22 st.global.f64 [%rd4], %fd2;
23 ret;
24 }
01-03 ヘッダ
07-08 関数のインターフェース部
.visible スコープ(global)
.entry 関数の開始
.param 引数
.u64 unsigned int 64bit
_Z4axpyPd_param_0 引数ポインタ
10-12 レジスタ
.b32 untyped 32bit r1,r2
.f64 float 64bit fd1,,fd3
.b64 untyped 64bit rd1,,rd5
15-23(関数本体)は
次スライドで詳しく
- 9. PTX(関数本体)
ver.1.2 9
15 ld.param.u64 %rd1, [_Z4axpyPd_param_0];
16 cvta.to.global.u64 %rd2, %rd1;
17 mov.u32 %r1, %tid.x;
18 mul.wide.s32 %rd3, %r1, 8;
19 add.s64 %rd4, %rd2, %rd3;
20 ld.global.f64 %fd1, [%rd4];
21 add.f64 %fd2, %fd1, 0d4020000000000000;
22 st.global.f64 [%rd4], %fd2;
23 ret;
__global__ void axpy(double *y){
int i = threadIdx.x;
double a;
a = 8.0;
y[i] = y[i] + a;
}
15 引数の先頭アドレスをレジスタrd1に
ロード、符号無し64bit
16 rd1をrd2(グローバルメモリ上のアド
レス)に変換
17 スレッドidをr1にコピー、符号無し
32bit
18 r1と8(倍精度なので8バイト刻み)の
積をrd3に代入 64bit(wide)で確保
19 rd2にrd3を加えてrd4に代入(各スレッ
ドが担当するデータのアドレス)
20 rd4が指すデータをfd1(グローバルメ
モリ)にロード、64bit浮動小数点
21 fd1に8.0d0を加えてfd2に代入
22 fd2をrd4が指すデータに代入
23 関数戻り
- 10. 補助情報を出した場合
ver.1.2 10
.version 6.0
…
.visible .entry _Z4axpyPd(
.param .u64 _Z4axpyPd_param_0
)
{
…
ld.param.u64 %rd1, [_Z4axpyPd_param_0];
///home/takateru.yamagishi/00CUDA/06mcBMT/axpy/CUDAC/axpy.CC.a01.cu:10 int i = threadIdx.x;
.loc 1 10 9
cvta.to.global.u64 %rd2, %rd1;
mov.u32 %r1, %tid.x;
///home/takateru.yamagishi/00CUDA/06mcBMT/axpy/CUDAC/axpy.CC.a01.cu:14 y[i] = y[i] + a;
.loc 1 14 3
mul.wide.s32 %rd3, %r1, 8;
…
st.global.f64 [%rd4], %fd2;
///home/takateru.yamagishi/00CUDA/06mcBMT/axpy/CUDAC/axpy.CC.a01.cu:16 }
.loc 1 16 1
ret;
}
.file 1 "/home/takateru.yamagishi/00CUDA/06mcBMT/axpy/CUDAC/axpy.CC.a01.cu", 1538446405, 895
.file 2 "/opt/cuda/9.0/bin/..//include/cuda_device_runtime_api.h", 1522911204, 14588
.file 3 "/opt/cuda/9.0/bin/..//include/cuda_runtime.h", 1522911204, 88442
loc指示行はファイル関連情
報を示している
.loc 1 10 9
File 1の10行目の9列目
- 12. IF文を含むカーネル(PTX)
ver.1.2 12
01 .reg .pred %p<2>;
02 .reg .b32 %r<2>;
03 .reg .f64 %fd<4>;
04 .reg .b64 %rd<5>;
05 ld.param.u64 %rd1, [_Z4axpyPd_param_0];
06 cvta.to.global.u64 %rd2, %rd1;
07 mov.u32 %r1, %tid.x;
08 mul.wide.s32 %rd3, %r1, 8;
09 add.s64 %rd4, %rd2, %rd3;
10 ld.global.f64 %fd1, [%rd4];
11 setp.gt.f64 %p1, %fd1, 0d0000000000000000;
12 add.f64 %fd2, %fd1, 0d4020000000000000;
13 selp.f64 %fd3, %fd2, 0d0000000000000000, %p1;
14 st.global.f64 [%rd4], %fd3;
15 ret;
01 predicate(bool)レジスタ p1, p2
02 untyped32bitレジスタ r1, r2
03 float64bitレジスタ fd1-fd4
04 untyped64bitレジスタ rd1-rd5
05 引数の先頭アドレスをレジスタrd1に
ロード、符号無し64bit
06 rd1をrd2(グローバルメモリ上のアド
レス)に変換
07 スレッドidをr1にコピー、符号無し
32bit
08 r1の8(倍精度なので8バイト刻み)の
積をrd3に代入 64bit(wide)で確保
09 rd2にrd3を加えてrd4に代入(各ス
レッドが担当するデータのアドレス)
10 rd4が指すデータをfd1(グローバルメ
モリ)にロード、64bit浮動小数点
11 p1 = (fd1 > 0d0)
12 fd1に8.0d0を加えてfd2に代入
13 fd3 = p1 ? fd2 : 0d0
14 fd3をrd4が指すデータに代入
15 関数戻り
- 13. forループを含むカーネル 1
ver.1.2 13
__global__ void axpy(double *y)
{
int i = threadIdx.x;
double a;
a = 8.0;
int NN = 100;
for(int ii=0; ii<NN; ii++)
{
y[i] = y[i] + a;
}
}
.reg .b32 %r<2>;
.reg .f64 %fd<102>;
.reg .b64 %rd<5>;
ld.param.u64 %rd1, [_Z4axpyPd_param_0];
cvta.to.global.u64 %rd2, %rd1;
mov.u32 %r1, %tid.x;
mul.wide.s32 %rd3, %r1, 8;
add.s64 %rd4, %rd2, %rd3;
ld.global.f64 %fd1, [%rd4];
add.f64 %fd2, %fd1, 0d4020000000000000;
add.f64 %fd3, %fd2, 0d4020000000000000;
add.f64 %fd4, %fd3, 0d4020000000000000;
add.f64 %fd5, %fd4, 0d4020000000000000;
add.f64 %fd6, %fd5, 0d4020000000000000;
add.f64 %fd7, %fd6, 0d4020000000000000;
add.f64 %fd8, %fd7, 0d4020000000000000;
add.f64 %fd9, %fd8, 0d4020000000000000;
…
add.f64 %fd97, %fd96, 0d4020000000000000;
add.f64 %fd98, %fd97, 0d4020000000000000;
add.f64 %fd99, %fd98, 0d4020000000000000;
add.f64 %fd100, %fd99, 0d4020000000000000;
add.f64 %fd101, %fd100, 0d4020000000000000;
st.global.f64 [%rd4], %fd101;
ret;
}
ループ展開されていた
ループの回転数が固定の場合
- 14. forループを含むカーネル 2
ver.1.2 14
__global__ void axpy(double *y,
int NN)
{
int i = threadIdx.x;
double a;
a = 8.0;
// int NN = 100;
for(int ii=0; ii<NN; ii++)
{
y[i] = y[i] + a;
}
}
.reg .pred %p<7>;
.reg .b32 %r<18>;
.reg .f64 %fd<22>;
.reg .b64 %rd<5>;
ld.param.u64 %rd2, [_Z4axpyPdi_param_0];
ld.param.u32 %r7, [_Z4axpyPdi_param_1];
setp.lt.s32 %p1, %r7, 1;
@%p1 bra BB0_12;
-------------------------中略-------------------------------
BB0_10:
add.f64 %fd13, %fd21, 0d4020000000000000;
add.f64 %fd14, %fd13, 0d4020000000000000;
add.f64 %fd15, %fd14, 0d4020000000000000;
add.f64 %fd21, %fd15, 0d4020000000000000;
add.s32 %r17, %r17, 4;
setp.lt.s32 %p6, %r17, %r7;
@%p6 bra BB0_10;
BB0_11:
st.global.f64 [%rd1], %fd21;
BB0_12:
ret;
}
ループの回転数は動的に決定
回転数は動的なのでループ
展開は無し
プレディケード
レジスタの用意
• プレディケードレジスタを
基にラベルへのjump命令
• bra命令はレベルにジャンプ
して(PCをそこに移して)
実行を継続する命令となる
- 17. 指示行に使う指示子
ver.1.2 17
.reg レジスタの確保 .param 関数の引数
.version PTXのversion
.extern/visible/weak 関数のスコープ
NVIDIA公式マニュアルから引用
PTXは命令と指示行(以下の指示子で始まる)で構成される
これらの中には先頭以外で使われるものもある
- 22. CUDA CとCUDA Fortran
この二つの違いはPTXよりも上位
CUDA FortranからCUDA Cライクに変換された後は基本違
いなし(その後は共にnvccでコンパイルされる)
CUDA Fortranの方が冗長な処理が入っていることも
特に配列のidexとか
余計なコストになるかもしれないが今回は評価していない
今回扱うような簡単なケースではPTXにそれほど大きな違い
は無かった
関連論文
Satake et al. (2012)
熱伝導方程式の最適化 CUDA CとCUDA FのPTX比較も通じて
2012年の結果故最新のコンパイラにはそのままは当てはまらない
可能性あり
重要なことは違いが起こりうることを知っておくこと
評価が重要でそのためのアセンブリ言語基礎
ver.1.2 22
- 23. 比較コード
ver.1.2 23
__global__ void axpy(double *y){
int i = threadIdx.x;
double a;
a = 8.0;
y[i] = y[i] + a;
}
attributes(global) SUBROUTINE axpy(y)
REAL(8), device :: y(N)
REAL(8) :: a
INTEGER :: i
i = threadidx%x
a = 8d0
y(i) = y(i) + a
END SUBROUTINE axpy
- 24. PTXの比較
ver.1.2 24
.version 6.0
.target sm_60
.address_size 64
// .globl _Z4axpyPd
.visible .entry _Z4axpyPd(
.param .u64 _Z4axpyPd_param_0
)
{
.reg .b32 %r<2>;
.reg .f64 %fd<3>;
.reg .b64 %rd<5>;
ld.param.u64 %rd1, [_Z4axpyPd_param_0];
cvta.to.global.u64 %rd2, %rd1;
mov.u32 %r1, %tid.x;
mul.wide.s32 %rd3, %r1, 8;
add.s64 %rd4, %rd2, %rd3;
ld.global.f64 %fd1, [%rd4];
add.f64 %fd2, %fd1, 0d4020000000000000;
st.global.f64 [%rd4], %fd2;
ret;
}
.version 6.0
.target sm_60
.address_size 64
// .globl sub_axpy_
.visible .entry sub_axpy_(
.param .u64 sub_axpy__param_0
)
{
.reg .b32 %r<3>;
.reg .f64 %fd<3>;
.reg .b64 %rd<5>;
ld.param.u64 %rd1, [sub_axpy__param_0];
mov.u32 %r1, %tid.x;
shl.b32 %r2, %r1, 3;
cvt.s64.s32 %rd2, %r2;
cvta.to.global.u64 %rd3, %rd1;
add.s64 %rd4, %rd3, %rd2;
ld.global.f64 %fd1, [%rd4];
add.f64 %fd2, %fd1, 0d4020000000000000;
st.global.f64 [%rd4], %fd2;
ret;
}
CUDA C CUDA Fortran
- 26. SASSの例
ver.1.2 26
code for sm_60
Function : _Z4axpyPd
.headerflags @"EF_CUDA_SM60 EF_CUDA_PTX_SM(EF_CUDA_SM60)"
/* 0x083fc400e3e007f6 */
/*0008*/ MOV R1, c[0x0][0x20]; /* 0x4c98078000870001 */
/*0010*/ S2R R0, SR_TID.X; /* 0xf0c8000002170000 */
/*0018*/ ISCADD R2.CC, R0.reuse, c[0x0][0x140], 0x3; /* 0x4c18818005070002 */
/* 0x001dc800fc4007ec */
/*0028*/ SHR R0, R0, 0x1d; /* 0x3829000001d70000 */
/*0030*/ IADD.X R3, R0, c[0x0][0x144]; /* 0x4c10080005170003 */
/*0038*/ LDG.E.64 R4, [R2]; /* 0xeed5200000070204 */
/* 0x001f98011e204714 */
/*0048*/ DADD R4, R4, 8; /* 0x3870004020070404 */
/*0050*/ STG.E.64 [R2], R4; /* 0xeedd200000070204 */
/*0058*/ NOP; /* 0x50b0000000070f00 */
/* 0x001f8000ffe007ff */
/*0068*/ EXIT; /* 0xe30000000007000f */
/*0070*/ BRA 0x70; /* 0xe2400fffff87000f */
/*0078*/ NOP; /* 0x50b0000000070f00 */
前掲のPTX出力例と比較
- 27. SASSの例
ver.1.2 27
code for sm_60
Function : _Z4axpyPd
.headerflags @"EF_CUDA_SM60 EF_CUDA_PTX_SM(EF_CUDA_SM60)"
/* 0x083fc400e3e007f6 */
/*0008*/ MOV R1, c[0x0][0x20]; /* 0x4c98078000870001 */
/*0010*/ S2R R0, SR_TID.X; /* 0xf0c8000002170000 */
/*0018*/ ISCADD R2.CC, R0.reuse, c[0x0][0x140], 0x3; /* 0x4c18818005070002 */
/* 0x001dc800fc4007ec */
/*0028*/ SHR R0, R0, 0x1d; /* 0x3829000001d70000 */
/*0030*/ IADD.X R3, R0, c[0x0][0x144]; /* 0x4c10080005170003 */
/*0038*/ LDG.E.64 R4, [R2]; /* 0xeed5200000070204 */
/* 0x001f98011e204714 */
/*0048*/ DADD R4, R4, 8; /* 0x3870004020070404 */
/*0050*/ STG.E.64 [R2], R4; /* 0xeedd200000070204 */
/*0058*/ NOP; /* 0x50b0000000070f00 */
/* 0x001f8000ffe007ff */
/*0068*/ EXIT; /* 0xe30000000007000f */
/*0070*/ BRA 0x70; /* 0xe2400fffff87000f */
/*0078*/ NOP; /* 0x50b0000000070f00 */
MOV: Move
S2R: Move Special
Register to Register
ISCADD: Scaled Integer
Addition
SHR: Shift Right
IADD: Integer Addition
LDG: Load from Global
Memory
DADD: FP64 Add
STG: Store to Global
Memory
前掲のPTX出力例と比較
- 30. PTX・SASSの活用例
FMA利用の確認など(from CUDA Fortran for Scientists and Engineers)
#pragma unrollの問題(三木洋平, 2015)
PTXでは入っているがSASSでは消えた
手で展開したそうです
レジスタスピルの改善
本気で追い込むにはSASSじゃないとダメらしい
ver.1.2 30
attributes(global) subroutine k(a,b,c)
implicit none
real :: a, b, c
c = a*b+c
end subroutine k
fma.rn.f32 %f4, %f2, %f3, %f1;
mul.rn.f32 %f4, %f2, %f3;
add.f32 %f5, %f1, %f4;
-Mcuda=nofma指定時
- 32. CUDA Fortran + intent(in)
参照のみと指定された
変数xはテクスチャメ
モリに確保される
陽にテクスチャを指定
することも可能
ver.1.2 32
attributes(global) SUBROUTINE daxpy(n, x, y)
INTEGER, value :: n
REAL(8), device, intent(in) :: x(n)
REAL(8), device :: y(n)
REAL(8) :: a
INTEGER :: i
i = threadidx%x
a = 8d0
y(i) = a * x(i) + y(i)
end subroutine daxpy
[参考 CUDA Cでの利用]
• 組み込み関数__ldg利用(陽に指定)
• グローバルメモリのポインタに対して
restrict修飾子適用(コンパイラ判断)
[モジュールにて宣言]
real, texture, pointer :: t(:)
[利用側にてbindさせる]
real, target, device :: a(n)
t => a ! texureがdeviceを指す
- 33. PTXでの確認
ver.1.2 33
.visible .entry test_daxpy_(
.param .u32 test_daxpy__param_0,
.param .u64 test_daxpy__param_1,
.param .u64 test_daxpy__param_2
)
{
.reg .b32 %r<2>;
.reg .f64 %fd<4>;
.reg .b64 %rd<8>;
ld.param.u64 %rd1, [test_daxpy__param_1];
ld.param.u64 %rd2, [test_daxpy__param_2];
cvta.to.global.u64 %rd3, %rd2;
cvta.to.global.u64 %rd4, %rd1;
mov.u32 %r1, %tid.x;
mul.wide.s32 %rd5, %r1, 8;
add.s64 %rd6, %rd4, %rd5;
ld.global.nc.f64 %fd1, [%rd6];
add.s64 %rd7, %rd3, %rd5;
ld.global.f64 %fd2, [%rd7];
fma.rn.f64 %fd3, %fd1, 0d4020000000000000, %fd2;
st.global.f64 [%rd7], %fd3;
ret;
}
ld.global.nc [※PTXマニュアルから]
Load a register variable from global
state space via non-coherent cache.
intent(in)指定しない場合:
ld.global.f64 %fd1, [%rd6];
- 34. SASSでの確認
ver.1.2 34
code for sm_60
Function : test_daxpy_
.headerflags @"EF_CUDA_SM60 EF_CUDA_PTX_SM(EF_CUDA_SM60)"
/* 0x083fc400e3e007f6 */
/*0008*/ MOV R1, c[0x0][0x20] ; /* 0x4c98078000870001 */
/*0010*/ S2R R0, SR_TID.X ; /* 0xf0c8000002170000 */
/*0018*/ SHL R4, R0.reuse, 0x3 ; /* 0x3848000000370004 */
/* 0x001f8800fcc007e5 */
/*0028*/ SHR R0, R0, 0x1d ; /* 0x3829000001d70000 */
/*0030*/ IADD R6.CC, R4, c[0x0][0x148] ; /* 0x4c10800005270406 */
/*0038*/ IADD.X R7, R0, c[0x0][0x14c] ; /* 0x4c10080005370007 */
/* 0x001f8800fec007f0 */
/*0048*/ { IADD R4.CC, R4, c[0x0][0x150] ; /* 0x4c10800005470404 */
/*0050*/ LDG.E.CI.64 R2, [R6] }
/* 0xeed5a00000070602 */
/*0058*/ IADD.X R5, R0, c[0x0][0x154] ; /* 0x4c10080005570005 */
/* 0x003fc420e28007b5 */
/*0068*/ LDG.E.64 R8, [R4] ; /* 0xeed5200000070408 */
/*0070*/ DFMA R2, R2, 8, R8 ; /* 0x3670044020070202 */
/*0078*/ STG.E.64 [R4], R2 ; /* 0xeedd200000070402 */
/* 0x001f8000ffe007ff */
/*0088*/ EXIT ; /* 0xe30000000007000f */
/*0090*/ BRA 0x90 ; /* 0xe2400fffff87000f */
/*0098*/ NOP; /* 0x50b0000000070f00 */
/* 0x001f8000fc0007e0 */
/*00a8*/ NOP; /* 0x50b0000000070f00 */
/*00b0*/ NOP; /* 0x50b0000000070f00 */
/*00b8*/ NOP; /* 0x50b0000000070f00 */
テクスチャメモリへのロードを
指定するオプションCI
※公式マニュアルには説明無し
intent(in)指定しない場合:
LDG.E.64 R2, [R6]
- 35. Reductionでの命令レベルの並列性
ver.1.2 35
__inline__ __device__
int warpReduceSum(int val) {
for (int offset = warpSize/2; offset >
0; offset /= 2)
val += __shfl_down(val, offset);
return val;
}
__inline__ __device__
int3 warpReduceSumTriple(int3 val) {
for (int offset = warpSize / 2; offset >
0; offset /= 2) {
val.x += __shfl_down(val.x, offset);
val.y += __shfl_down(val.y, offset);
val.z += __shfl_down(val.z, offset);
}
return val;
}
Faster Parallel Reductions on Kepler https://devblogs.nvidia.com/faster-parallel-reductions-kepler/
SASS for a single shuffle reductions
SASS for interleaved shuffle reductions
依存性のある処理を続けて配置させないことでストールを回避
- 37. 参考文献
CUDA C Programming Guide, NVIDIA
CUDAコンパイルのフローなど
CUDA BINARY UTILITIES, NVIDIA
cuobjdumpコマンド, SASSの解説
PARALLEL THREAD EXECUTION ISA, NVIDIA
PTXの詳細な解説
CUDA Fortran for Scientists and Engineers, Gregory
Ruetschら, Morgan Kaufmann
PTXでのFMA等命令の確認について簡単に記載
GPUプログラミング入門, 伊藤ら, 講談社
Satake et al. (2012)も含む
CUDAプログラミング入門, 青山幸也
PTXの読み方を詳細に解説
SASSの読み方 http://int.main.jp/txt/sass/
SASSを解説した唯一の和文公開文書
ver.1.2 37