
Tritonã®çŽ¹ä» - ãã¥ãŒã©ã«ãããã¯ãŒã¯ã®ããã®GPUããã°ã©ãã³ã°
以äžã®èšäºãåèã«ããŠæžããŠãŸãã
ã»Introducing Triton: Open-Source GPU Programming for Neural Networks
1. ã¯ããã«
ãTriton 1.0ãã¯ããªãŒãã³ãœãŒã¹ã®Pythonã©ã€ã¯ãªããã°ã©ãã³ã°èšèªã§ãCUDAã®çµéšããªãç ç©¶è ã§ããå€ãã®å Žåãå°éå®¶ãšåçã®é«å¹çãªGPUã³ãŒããæžãããšãã§ããŸãã
äŸãã°ãå€ãã®GPUããã°ã©ããã§ããªãcuBLASã®æ§èœã«å¹æµããFP16è¡åä¹ç®ã«ãŒãã«ãã25è¡ä»¥äžã®ã³ãŒãã§æžãããšãã§ããŸããç§ãã¡ã®ç ç©¶è ã¯ããã§ã«ãã®ããŒã«ã䜿ã£ãŠãåçã®Torchå®è£ ããããæå€§2åå¹ççãªã«ãŒãã«ãäœæããŠããŸããç§ãã¡ã¯ãã³ãã¥ããã£ãšååããŠã誰ããGPUããã°ã©ãã³ã°ãå©çšã§ããããã«ãªãããšã楜ãã¿ã«ããŠããŸãã
2. Triton
深局åŠç¿ã®åéã«ãããæ°ããç ç©¶ã¢ã€ãã¢ã¯ãäžè¬çã«ãã€ãã£ããã¬ãŒã ã¯ãŒã¯ã®æŒç®åãçµã¿åãããŠå®è£
ãããŠããŸãããã®æ¹æ³ã¯äŸ¿å©ã§ãããå€ãã®äžæçãªãã³ãœã«ãäœæïŒãŸãã¯ç§»åïŒããå¿
èŠããããå€§èŠæš¡ãªãã¥ãŒã©ã«ãããã¯ãŒã¯ã®ããã©ãŒãã³ã¹ãäœäžãããå¯èœæ§ããããŸãããã®ãããªåé¡ã¯ãå°çšã®GPU ã«ãŒãã«ãäœæããããšã§è»œæžã§ããŸãããGPUããã°ã©ãã³ã°ã¯éåžžã«è€éã§é£ãããã®ã§ããããã§ç§ãã¡ã¯ãæè¿éçºãããèšèªããã³ã³ã³ãã€ã©ã§ãããTritonããæ¡åŒµã»æ¹è¯ããããšã«ããŸããã
3. GPUããã°ã©ãã³ã°ã®èª²é¡
æè¿ã®GPUã®ã¢ãŒããã¯ãã£ã¯ã倧ããåããŠãDRAMããSRAMããALUãã®3ã€ã®èŠçŽ ããæ§æãããŠãããCUDAã³ãŒããæé©åããéã«ã¯ãããããã®èŠçŽ ãèæ ®ããå¿ èŠããããŸãã
ã»DRAMããã®ã¡ã¢ãªè»¢éã¯ãææ°ã®ã¡ã¢ãªã€ã³ã¿ãŒãã§ãŒã¹ã®åºããã¹å¹ ãæŽ»çšããããã«ã倧ããªãã©ã³ã¶ã¯ã·ã§ã³ã«ãŸãšããå¿ èŠããããŸãã
ã»ããŒã¿ãåå©çšããéã«ã¯ãSRAMã«æåã§é ããå ±æã¡ã¢ãªãã³ã¯ã®ã³ã³ããªã¯ããæå°éã«æããããã«ç®¡çããªããã°ãªããŸããã
ã»Streaming MultiprocessorsïŒSMïŒå ã§ã®èšç®ã®åå²ãšã¹ã±ãžã¥ãŒãªã³ã°ãæ éã«è¡ãå¿ èŠããããŸããåœä»€/ã¹ã¬ããã¬ãã«ã®äžŠååŠçãä¿é²ããç¹æ®ãªçšéã®ALUïŒäŸïŒãã³ãœã«ã³ã¢ïŒã掻çšããããã«ããŠããŸãã
ãããã®èŠçŽ ããã¹ãŠæšè«ããããšã¯ãé·å¹Žã®çµéšãæã€çç·Žã®CUDAããã°ã©ããŒã«ãšã£ãŠãå°é£ãªããšã§ãããTritonãã®ç®çã¯ããããã®æé©åãå®å šã«èªååããéçºè ã䞊åã³ãŒãã®ãã€ã¬ãã«ãªããžãã¯ã«éäžã§ããããã«ããããšã§ãããTritonãã¯å¹ åºãå¿çšãç®çãšããŠãããããSMéã®äœæ¥ãèªåçã«ã¹ã±ãžã¥ãŒãªã³ã°ããããšã¯ãªããéèŠãªã¢ã«ãŽãªãºã ã®æ€èšïŒã¿ã€ãªã³ã°ãSMéã®åæãªã©ïŒã¯éçºè ã®å€æã«å§ããããŸãã
4. ããã°ã©ãã³ã°ã¢ãã«
ãDomain Specific LanguagesããšãJITã³ã³ãã€ã©ãã®äžã§ããTritonãã¯ãããããNumbaãã«æã䌌ãŠããŸããã«ãŒãã«ã¯Pythonã®é¢æ°ãšããŠå®çŸ©ãããããããã€ã³ã¹ã¿ã³ã¹ã®ã°ãªããäžã§ç°ãªãprogram_idã§åæã«èµ·åãããŸãããããã以äžã®ã³ãŒãã«ç€ºãããŠããããã«ã䌌ãŠããã®ã¯ããã ãã§ã¯ãããŸããããTritonãã¯ããSingle Instruction, Multiple Threadã (SIMT)ã®å®è¡ã¢ãã«ã§ã¯ãªãããããã¯ïŒ2ã®çޝä¹ã§æ§æãããå°ããªé åïŒã«å¯Ÿããæäœã«ãã£ãŠãã€ã³ã¹ã¿ã³ã¹å ã®äžŠåæ§ãå®çŸããŸããããã«ãããTritonãã¯ãCUDAã¹ã¬ãããããã¯å ã§ã®äžŠè¡åŠçã«é¢ããåé¡ïŒã¡ã¢ãªã®åäœãå ±æã¡ã¢ãªã®åæ/è¡çªããã³ãœã«ã³ã¢ã®ã¹ã±ãžã¥ãŒãªã³ã°ãªã©ïŒã广çã«æœè±¡åããŸãã
ã»Tritonã§ã®ãã¯ãã«å ç®
BLOCK = 512
# ããã¯Numbaã®GPUã«ãŒãã«ã§ã
# ãã®é¢æ°ã®æ§ã
ãªã€ã³ã¹ã¿ã³ã¹ã䞊è¡ããŠå®è¡ãããå ŽåããããŸã
@jit
def add(X, Y, Z, N):
# Numba / CUDAã§ã¯ãåã«ãŒãã«ã€ã³ã¹ã¿ã³ã¹èªäœãSIMTå®è¡ã¢ãã«ã䜿çšããŸã
# ããã§ãthreadIdxã®æ§ã
ãªå€ã«å¯ŸããŠåœä»€ã䞊è¡ããŠå®è¡ãããŸã
ã tid = threadIdx.x
ã bid = blockIdx.x
ã # ã¹ã«ã©ãŒã€ã³ããã¯ã¹
ã idx = bid * BLOCK + tid
ã if id < N:
ã # Numbaã«ã¯ãã€ã³ã¿ããããŸãã
ã # ZãXãYã¯å¯ãªãã³ãœã«ã§ã
ã Z[idx] = X[idx] + Y[idx]
...
grid = (ceil_div(N, BLOCK),)
block = (BLOCK,)
add[grid, block](x, y, z, x.shape[0])BLOCK = 512
# ããã¯Tritonã®GPUã«ãŒãã«ã§ãã
# ãã®é¢æ°ã®æ§ã
ãªã€ã³ã¹ã¿ã³ã¹ã䞊è¡ããŠå®è¡ãããå ŽåããããŸã
@jit
def add(X, Y, Z, N):
# Tritonã§ã¯ãåã«ãŒãã«ã€ã³ã¹ã¿ã³ã¹ãåäžã®ã¹ã¬ããã§ãããã¯æäœãå®è¡ããŸã
# threadIdxã«é¡äŒŒããæ§é ã¯ãããŸããã
pid = program_id(0)
# block of indices
idx = pid * BLOCK + arange(BLOCK)
mask = idx < N
# Tritonã¯ãã€ã³ããã¯ã¹æŒç®åã§ã¯ãªããã€ã³ã¿æŒç®ã䜿çšããŸã
x = load(X + idx, mask=mask)
y = load(Y + idx, mask=mask)
store(Z + idx, x + y, mask=mask)
...
grid = (ceil_div(N, BLOCK),)
# ã¹ã¬ãããããã¯ãªã
add[grid](x, y, z, x.shape[0])ããã¯ã䞊åèšç®ïŒã€ãŸãèŠçŽ åäœã®èšç®ïŒã«ã¯ç¹ã«åœ¹ç«ããªããããããŸããããããè€éãªGPUããã°ã©ã ã®éçºãå€§å¹ ã«ç°¡çŽ åã§ããŸãã
äŸãã°ãèåãœããããã¯ã¹ã«ãŒãã«ã®å ŽåïŒäžå³ïŒãåã€ã³ã¹ã¿ã³ã¹ã¯äžããããå
¥åãã³ãœã«Â XâR^{MÃN} ã®ç°ãªãè¡ãæ£èŠåããŸãããã®äžŠååææ³ã®æšæºçãªCUDAå®è£
ã¯ãã¹ã¬ããéã§åãè¡ã®Xãåæã«åæžããéã«æç€ºçãªåæãå¿
èŠãšãããããèšè¿°ãå°é£ã§ãããTritonãã§ã¯ãåã«ãŒãã«ã€ã³ã¹ã¿ã³ã¹ã察象ãšãªãè¡ãããŒãããNumPyã®ãããªããªããã£ããçšããŠé 次æ£èŠåããããšã§ããã®è€éãã®ã»ãšãã©ãè§£æ¶ãããŸãã
ã»Tritonã®Fused softmax
import triton
import triton.language as tl
@triton.jit
def softmax(Y, stride_ym, stride_yn, X, stride_xm, stride_xn, M, N):
# è¡ã€ã³ããã¯ã¹
m = tl.program_id(0)
# åã€ã³ããã¯ã¹
# ãã®ç¹å®ã®ã«ãŒãã«ã¯ãBLOCK_SIZEåããå°ãªãè¡åã«å¯ŸããŠã®ã¿æ©èœããŸã
BLOCK_SIZE = 1024
n = tl.arange(0, BLOCK_SIZE)
# ããŒãããããã¹ãŠã®èŠçŽ ã®ã¡ã¢ãªã¢ãã¬ã¹ã¯æ¬¡ã®ããã«èšç®ã§ããŸã
X = X + m * stride_xm + n * stride_xn
# å
¥åããŒã¿ãããŒãããŸãã ç¯å²å€ã®èŠçŽ ã0ã§åããŸã
x = tl.load(X, mask=n < N, other=-float('inf'))
# æ°å€çã«å®å®ãããœããããã¯ã¹ãèšç®ãã
z = x - tl.max(x, axis=0)
num = tl.exp(z)
denom = tl.sum(num, axis=0)
y = num / denom
# Yã«æžãæ»ã
Y = Y + m * stride_ym + n * stride_yn
tl.store(Y, y, mask=n < N)
import torch
# å
¥å/åºåãã³ãœã«ãå²ãåœãŠãŸã
X = torch.normal(0, 1, size=(583, 931), device='cuda')
Y = torch.empty_like(X)
# SPMDèµ·åã°ãªãã
grid = (X.shape[0], )
# GPUã«ãŒãã«ããšã³ãã¥ãŒããŸã
softmax[grid](Y, Y.stride(0), Y.stride(1),
X, X.stride(0), X.stride(1),
X.shape[0] , X.shape[1])ãTriton JITãã§ã¯ïŒXãšYããã³ãœã«ã§ã¯ãªããã€ã³ã¿ãšããŠæ±ã£ãŠããããšã«æ³šæããŠãã ãããããè€éãªããŒã¿æ§é ïŒãããã¯ã»ã¹ããŒã¹ã»ãã³ãœã«ãªã©ïŒã«å¯Ÿå¿ããããã«ã¯ãã¡ã¢ãªã¢ã¯ã»ã¹ãäœã¬ãã«ã§å¶åŸ¡ããããšãéèŠã ãšèããããã§ãã
éèŠãªã®ã¯ããã®ãœããããã¯ã¹ã®å®è£ ã§ã¯ãæ£èŠåããã»ã¹å šäœãéããŠãXè¡ãSRAMã«ä¿æããããšã§ãããŒã¿ã®åå©çšãå¯èœã«ããŠããããšã§ããããã¯PyTorchå éšã®CUDAã³ãŒããšã¯ç°ãªããŸããäžæã¡ã¢ãªã䜿çšããããšã§ãããäžè¬çã«ãªããŸãããé床ã¯èããäœäžããŸãïŒä»¥äžïŒãããã§éèŠãªã®ã¯ããTritonããæ¬è³ªçã«åªããŠãããšããããšã§ã¯ãªããæ±çšã©ã€ãã©ãªãããã¯ããã«é«éãªç¹æ®ã«ãŒãã«ã®éçºã容æã«ããŠãããšããããšã§ãã
Torch (v1.9) JITã®ããã©ãŒãã³ã¹ãäœãããšã¯ãé«ã¬ãã«ã®ãã³ãœã«æŒç®ã®ã·ãŒã±ã³ã¹ããCUDAã³ãŒããèªåçæããããšã®é£ããã瀺ããŠããŸãã
ã»Torch JITãšFused softmax
@torch.jit.script
def softmax(x):
x_max = x.max(dim=1)[0]
z = x - x_max[:, None]
numerator = torch.exp(x)
denominator = numerator.sum(dim=1)
return numerator / denominator[:, None]5. è¡åã®ä¹ç®
èŠçŽ åäœã®æŒç®ãåæžã®ããã«èåããã«ãŒãã«ãæžããããšã¯éèŠã§ããããã¥ãŒã©ã«ãããã¯ãŒã¯ã«ãããè¡åä¹ç®ã¿ã¹ã¯ã®éèŠæ§ãèãããšååã§ã¯ãããŸãããçµæçã«ããTritonãã¯ãããã®åŠçã«ãéåžžã«æå¹ã§ãããã25è¡ã®Pythonã³ãŒãã§ããŒã¯æ§èœãéæããŸãããäžæ¹ãåæ§ã®ããšãCUDAã§å®è£ ãããšãªããšãããå€ãã®åŽåãå¿ èŠãšããããã«ã¯æ§èœãäœäžããå¯èœæ§ããããŸãã
ã»Tritonã§ã®è¡åä¹ç®
@triton.jit
def matmul(A, B, C, M, N, K, stride_am, stride_ak,
stride_bk, stride_bn, stride_cm, stride_cn,
**META):
# ã¡ã¿ãã©ã¡ãŒã¿ãæœåºãã
BLOCK_M, GROUP_M = META['BLOCK_M'], META['GROUP_M']
BLOCK_N = META['BLOCK_N']
BLOCK_K = META['BLOCK_K']
# ããã°ã©ã ã¯ãL2ãããçãåäžãããããã«ã°ã«ãŒãåãããŸã
_pid_m = tl.program_id(0)
_pid_n = tl.program_id(1)
pid_m = _pid_m // GROUP_M
pid_n = (_pid_n * GROUP_M) + (_pid_m % GROUP_M)
# rm (ãŸãã¯rn)ã¯ãCã®è¡ïŒãŸãã¯åïŒã®ã€ã³ããã¯ã¹ã®ç¯å²ã瀺ããŸã
rm = pid_m * BLOCK_M + tl.arange(0, BLOCK_M)
rn = pid_n * BLOCK_N + tl.arange(0, BLOCK_N)
# rkã¯ãAïŒãŸãã¯BïŒã®åïŒãŸãã¯è¡ïŒã®ã€ã³ããã¯ã¹ã®ç¯å²ã瀺ããŸã
rk = tl.arange(0, BLOCK_K)
# AãšBã®æåã®ãããã¯ã®èŠçŽ ã®ã¡ã¢ãªã¢ãã¬ã¹ã¯ã
# numpyã¹ã¿ã€ã«ã®ãããŒããã£ã¹ãã䜿çšããŠèšç®ã§ããŸã
A = A + (rm[:, None] * stride_am + rk[None, :] * stride_ak)
B = B + (rk [:, None] * stride_bk + rn[None, :] * stride_bn)
# ã¢ãã¥ã ã¬ãŒã¿ãåæåããŠç¹°ãè¿ãæŽæ°ããŸã
acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
for k in range(K, 0, -BLOCK_K):
a = tl.load(A)
b = tl.load(B)
# ãããã¯ã¬ãã«ã®è¡åä¹ç®
acc += tl.dot(a, b)
# 次ã®å埩äžã«AãšBã®æ¬¡ã®ãããã¯ãããŒããããããã«ãã€ã³ã¿ãã€ã³ã¯ãªã¡ã³ãããŸã
A += BLOCK_K * stride_ak
B += BLOCK_K * stride_bk
# fuse leaky ReLU if desired
# acc = tl.where(acc >= 0, acc, alpha * acc)
# çµæãæžãæ»ã
C = C + (rm[:, None] * stride_cm + rn[None, :] * stride_cn)
mask = (rm[:, None] < M) & (rn[None, :] < N)
tl.store(C, acc, mask=mask)ææžãã®è¡åä¹ç®ã«ãŒãã«ã®éèŠãªå©ç¹ã®1ã€ã¯ãå ¥åïŒäŸïŒã¹ã©ã€ã¹ïŒãšåºåïŒäŸïŒLeaky ReLUïŒã®èåçãªå€æã«å¯Ÿå¿ããããã«ãå¿ èŠã«å¿ããŠã«ã¹ã¿ãã€ãºã§ããããšã§ãããTritonãã®ãããªã·ã¹ãã ããªããã°ãGPUããã°ã©ãã³ã°ã®å°éç¥èãæããªãéçºè ã«ãšã£ãŠãè¡åä¹ç®ã«ãŒãã«ã®èªæã§ãªã倿Žã¯æã®å±ããªããã®ã«ãªã£ãŠããŸãã§ãããã
6. ãã€ã¬ãã«ãªã·ã¹ãã ã¢ãŒããã¯ãã£
Tritonã®åªããæ§èœã¯ãLLVMããŒã¹ã®äžé衚çŸã§ããTriton-IRãäžå¿ãšããã¢ãžã¥ãŒã«åŒã®ã·ã¹ãã ã¢ãŒããã¯ãã£ã«ãã£ãŠå®çŸãããŠããŸãããã®äžé衚çŸã§ã¯ã倿¬¡å ã®å€ã®ãããã¯ããã¡ãŒã¹ãã¯ã©ã¹ãšããŠæ±ãããŸãã
ã»Tritonã®é«ã¬ãã«ã®ã¢ãŒããã¯ãã£
@triton.jit ãã³ã¬ãŒã¿ã¯ïŒPython 颿°ã®æœè±¡æ§ææšïŒASTïŒãèµ°æ»ãïŒäžè¬çãªSSAæ§ç¯ã¢ã«ãŽãªãºã ãçšã㊠Triton-IR ããªã³ã¶ãã©ã€ã§çæããŸããçæãããIRã³ãŒãã¯ãã³ã³ãã€ã©ããã¯ãšã³ãã«ãã£ãŠåçŽåãæé©åãèªå䞊ååãããåŸãé«å質㪠LLVM-IRãæçµçã«ã¯ PTX ã«å€æãããæè¿ã®NVIDIA GPUã§å®è¡ãããŸããçŸæç¹ã§ã¯ãCPUãšAMDã®GPUã¯ãµããŒããããŠããŸãããããã®åé¡ã解決ããããã®ã³ãã¥ããã£ãžã®è²¢ç®ãæè¿ããŸãã
7. ã³ã³ãã€ã©ããã¯ãšã³ã
Triton-IRãçšããŠãããã¯åãããããã°ã©ã ã衚çŸããããšã§ãã³ã³ãã€ã©ãæ§ã ãªéèŠãªããã°ã©ã æé©åãèªåçã«è¡ãããšãã§ããããšãããããŸãããäŸãã°ãèšç®éã®å€ããããã¯ã¬ãã«ã®æŒç®ïŒäŸïŒtl.dotïŒã®ãªãã©ã³ããèŠãããšã§ãããŒã¿ãèªåçã«å ±æã¡ã¢ãªã«æ ŒçŽããããæšæºçãªã©ã€ããã¹è§£ææè¡ãçšããŠå²ãåœãŠ/åæãè¡ãããšãã§ããŸãã
äžæ¹ããTritonãã®ããã°ã©ã ã¯ã(1)ç°ãªãã«ãŒãã«ã€ã³ã¹ã¿ã³ã¹ãåæã«å®è¡ããããšã§ãSMéã§å¹ççãã€èªåçã«äžŠååããããšãã§ããŸãããŸãã(2)SMå ã§ã¯ã以äžã®ããã«ãåãããã¯ã¬ãã«ã®æŒç®ã®å埩空éãåæããç°ãªãSIMDãŠãããã«é©åã«åå²ããããšã§ãå¹ççãã€èªåçã«äžŠååããããšãã§ããŸãã






