Index
2026-09-07 — Analysis

LingBot-VA B200 학습 커널 프로파일링 — FlexAttention vs flash-attn, MFU 7%

FurnitureBench | LingBot-VA(world-model) 학습, packing 구현 전 baseline 실측

TL;DR

~7%
MFU (B200 bf16)
0.18ms
토큰당 fwd+bwd
68k
1 GPU 최대 토큰
10~15%
실사용 토큰 비율

1 배경 / 목적

사용자 질문: "varlen packing이 실제로 flash 커널을 타는지, GPU당 몇 K 토큰까지 들어가는지, 스텝당 몇 초인지 실측했는지?" — 답은 실측한 적 없음이었다. 그동안 wan_va 학습 속도(4 GPU 19.2s/step, 8 GPU 34s/step)는 관측했지만, 그 시간이 attention 커널·GEMM·elementwise 중 어디에 쓰이는지, 그리고 GPU당 토큰 예산을 얼마나 쓰고 있는지는 블랙박스였다.

기존 한계: 8 GPU util이 accum 크기에 따라 50~99%로 널뛰는 이유, 그리고 packing 같은 최적화가 실제로 효과가 있을지 여부를 추측으로만 판단하고 있었다. packing 구현(다음 날 작업, 260907-lingbot_va_packing.html 참조)에 들어가기 전에 정량적 근거가 필요했다.

2 작업 내용

wan_va/train.py에 opt-in 프로파일 훅(LINGBOT_PROFILE=<n>)을 추가: micro-step별 토큰 수·fwd+bwd 시간·peak 메모리를 기록하고, 마지막 micro-step에서는 torch.profiler로 CUDA 커널 표를 뽑는다. 로그인 노드 GPU 1장(FSDP 없음, bf16)에서 furniture_all_smoke(all_low 데이터, batch 1, 6 step)를 2회 실행 — 로그인 디버깅 범위(≤100 step, ≤10분) 내의 프로파일링 작업이라 sbatch 불필요.

코드 확인: - attn_mode="flex" → torch.compile(flex_attention, dynamic=True) + block mask - flash-attn 2.8.3 설치돼 있으나 attn_mode="flashattn"(추론)에서만 참조 - q_varlen: 에피소드 1개를 "s n d -> 1 n s d"로 reshape, 에피소드 간 packing 없음 (batch=1 고정) - 프레임당 토큰 = (H/2)×(W/2) = (8/2)×(16/2) = 32 (2 cam 좌우 결합, patch 2×2)

3 결과 (B200 1 GPU, FSDP 없음, bf16)

micro-steplatent (C,F,H,W)토큰 수fwd+bwdpeak mem
0 (JIT 워밍업)48×240×8×169,15238–76s44.7 GB
1 (재컴파일)48×222×8×168,5044–6s47.9 GB
steady48×115–123×8×164,652–4,9400.85–0.96s79–81 GB
steady48×198×8×167,6401.27s84.7 GB
steady48×237–240×8×169,044–9,1521.62s86.4 GB
steady48×281–284×8×1610,628–10,7361.87–1.90s88.3 GB
선형 스케일: 토큰당 ≈0.18ms로 선형 — 블록-희소 attention이라 quadratic이 아님. 정적 메모리(가중치 9.5GB bf16 + fp32 master/grad/Adam) ≈71GB + 활성화 ≈1.6GB/1k 토큰. 1 GPU 180GB 기준 최대 ≈68k 토큰까지 가능(FSDP-8에서는 정적분이 줄어 80k+). 지금 micro-step당 5–11k 토큰만 사용 → GPU 토큰 예산의 10–15%만 쓰는 중.
flash-attn 미사용 확정: CUDA 커널 상위(5.2k 토큰 micro-step, 총 CUDA 811ms) — triton_tem_fused_zeros_1 133ms + triton_tem_fused_0 98ms (Inductor가 생성한 FlexAttention Triton 템플릿 fwd/bwd, 전체의 29%) / cuBLAS nvjet_* GEMM 3종 ≈200ms(25%) / elementwise 커널 ≈250ms(31%). flash_fwd/bwd 커널은 커널 표에 전혀 없음.
MFU ≈7%: 4.75B 파라미터 DiT, 6×params FLOP/토큰 ≈ 28 GFLOP/토큰 → 0.18ms/토큰 = ≈157 TFLOP/s ≈ B200 dense bf16의 ~7%. 작은 GEMM, 미융합 elementwise, micro-step당 dispatch 오버헤드가 지배적 원인으로 추정.

분산 run과 대조: 4 GPU accum 10 = 19.2s/step → 에피소드당 1.9s(util 92–99%), 8 GPU accum 16 = 34s/step → 에피소드당 2.1s(util 50–59%). 1 GPU 실측과 같은 수준이라 FSDP 통신 자체는 부차적 원인이고, micro-step 단위가 작아서 생기는 dispatch/미융합 오버헤드가 본질임을 확인.

4 Takeaway

의미

"packing이 flash 커널을 탄다"는 전제는 이 코드베이스에 해당하지 않는다 — FlexAttention Triton 커널이 돌고 있고, 애초에 에피소드 간 packing 자체가 없다(batch 1 고정). 유효 batch를 gradient accumulation으로만 키우는 현재 방식은 GPU당 토큰 예산의 1/8~1/10만 사용하므로, 에피소드 여러 개를 시퀀스 축으로 이어 붙이는 packing(FlexAttention mask_mod에 document id 추가 + collate에서 concat, GPU당 5–8 에피소드)을 구현하면 같은 유효 batch를 micro-step 1–2회로 처리할 수 있다는 정량적 근거를 확보했다. 토큰당 MFU가 7%로 낮은 편이라 packing으로 micro-step 수를 줄이면 dispatch 오버헤드 비중이 줄어 1.5–3× 속도 향상 여지가 있다고 판단(정확한 이득은 구현 후 실측 필요).

5 Next Steps

미해결 한계

MFU 7%의 정확한 병목 분해(작은 GEMM vs elementwise 미융합 vs dispatch 오버헤드 중 어느 비중이 가장 큰지)는 이번 프로파일링만으로는 확정 못함 — CUDA 커널 표의 3분할(29%/25%/31%)은 대략적인 카테고리 추정치.

다음 실험 (실행 완료)

packing 구현: lerobot_latent_dataset collate에서 K개 에피소드 concat + per-token doc id, FlexAttnFunc block mask에 doc_id[q]==doc_id[k] 조건 추가, action/latent target도 concat. 로그인 스모크로 정합성(loss 동일) 확인 후 e128 재학습에 적용 — 실측 결과는 같은 날 후속 카드 lingbot_va_packing 참조(8 GPU 34→23s/step, 1.5×; 토큰당 계산은 15%만 개선되어 이득 대부분이 FSDP micro-step 동기화 감소 16→4회에서 나옴을 확인).