feat(groot): GR00T N1.6 Thor parity fixes and opt-in 28.5 ms profile - #177
Conversation
Root-cause and fix 12 real bugs where the upstream N1.6 frontend
inherited openpi-family (Pi0/Pi0.5) vision/kernel assumptions that
do not hold for GR00T N1.6's HF behaviour:
1. Tokenization: reproduce Eagle chat template (system/user headers,
formalize, per-view image blocks) instead of bare encode()
2. Resolution: HF eval chain outputs 252x252, not 224
3. SigLIP attention scope: HF(sdpa) does cross-view full attention
on the packed 648-token sequence, not per-view
4. Patch flatten order: HF NaFlex uses (ph,pw,C), not (C,ph,pw)
5. Strided FMHA divergence on non-power-of-2 seq with real data:
parity mode routes SigLIP attention through torch sdpa
6. CKernelQwen3 diverges from HF on real sequences: parity mode
runs HF-native Qwen3Model (bf16, sdpa, graph-captured)
7. Wild pointer after re-capture: Qwen3 graph-captured LN referenced
local tensors; promote to persistent attributes + finiteness guard
8. adaLN chunk order reversed: HF proj_out_1 is (shift, scale)
9. Single-frame FP8 calibration too narrow: multi-frame calibrate
(current + 7 synthetic frames, percentile=99.9)
10. Prompt switch rejected after graph bake: detect change, reset
graph runtime, re-set prompt, re-capture
11. Idle-first-frame garbage: Thor GPU idle reset invalidates captured
graphs; add replay finiteness self-check + re-capture retry
12. Prompt-switch re-capture device-side assert: stale DiT static
buffers/indices not rebuilt; add to stale list
Precision vs HF eager: cos 0.999933 / maxd 0.059 (denormalized action).
No inference hyperparameters changed (4-step, 252x252, T=50, bf16).
Also adds tools/convert_groot_n16_hf_checkpoint.py for HF safetensors
to FlashRT layout conversion (Qwen3 16-layer truncation, DiT repack,
SigLIP mlp1 layout).
New CUDA kernels for the N1.6 Thor NVFP4 pipeline: - fused_fp4/silu_mul_fp4_sfa_bf16: SiLU(gate)*up (bf16) direct to NVFP4+SFA, bit-exact vs torch two-step chain - fused_fp4/dit_norm_fp4_sfa: AdaLN / no-affine LN / weighted RMSNorm direct to NVFP4+SFA (bf16 input variants) - gemm/fp4/cutlass_fp4_gemm_bias_bf16_sm100: bias / bias+residual / bias+tanh-GELU+fp4out epilogue variants - quantize/quantize_fp4_sfa_bf16: vectorized bf16 dynamic quantize - kernels/qk_norm_rope_rotate_half_bf16: fused per-head RMSNorm + rotate-half RoPE (bf16, in-place, one launch per Q/K) Performance rounds (no hyperparameter changes): - DiT NVFP4 fused epilogue: 36.6 -> 15.7 ms (8 kernels/layer) - Qwen3 fused norm/rope/GQA: 12.7 -> 5.0 ms (cos 0.999986) - SigLIP FA4 + fp4 encoder: 10.3 -> 6.9 ms (cos 0.999988) - SigLIP embeddings in-graph: 34 -> 28.5 ms (bit-exact) - E2E total: 130 -> 28.5 ms (4-step, 2-camera, 252x252, T=50) Bandwidth ceiling: Thor measured 252-255 GB/s (~93% of 273 spec); DiT 15.2 ms is weight-bandwidth-bound floor for this config. Tier switches (all default ON, independently fall back): FLASHRT_N16_DIT_FP4, FLASHRT_N16_QWEN3_FP4, FLASHRT_N16_SIGLIP_FP4, FLASHRT_N16_FA4
- docs/groot_n16_thor_sm110.md: single authoritative document covering architecture facts, 12-bug root-cause table, falsified hypotheses, full optimization record (130 -> 28.5 ms), roofline/bandwidth ceiling analysis (252-255 GB/s, ~93% of spec), precision tier switches, and verification methodology. - docs/groot_transformers5_weight_corruption.md: transformers>=5 silent weight corruption via _initialize_missing_keys re-randomizing SigLIP2 vision tower (282 tensors). One-line fix + integrity guard. - docs/thor_gpu_idle_reset_workaround.md: Thor GPU idle reset defect and three-layer CUDA Graph protection (keepalive, idle reinit, finiteness).
cutlass-dsl caches the device arch at import time. The previous code imported cutlass to check its version, then set CUTE_DSL_ARCH=sm_101a — too late; NVVM already cached sm_110a and ICEs on the hd256 2CTA kernel (introduced in flashrt-project#164, commit 7fd75d2). Fix: set CUTE_DSL_ARCH=sm_101a unconditionally before any cutlass import. Also revert the hd256 2CTA dispatch to SM100-only (the dedicated kernel was never validated on SM110) and restore the _fa4_trimmed lazy loader for BlackwellFusedMultiHeadAttentionForward. Verified: all-tier E2E on Thor — median 27.7 ms, p95 28.5 ms, actions finite, cos 0.999933 vs HF eager.
817cbf7 to
4cae6c0
Compare
|
Audit follow-up pushed in Resolved:
Validation completed:
The Thor host was offline during this follow-up. I have therefore not represented the cross-build as a Thor runtime result; one final Thor E2E/default-fallback/prompt-switch numerical run remains before merge. The PR body now states this distinction and documents the opt-in 28.5 ms profile accurately. |
|
Great Work!! @DXICM Could you help test whether this Thor behaves as expected? |
Final Thor E2E verification (Jetson AGX Thor, SM110) — PR head
|
| Config | E2E p50 | p95 | Notes |
|---|---|---|---|
Default FP8 224 (parity=False) |
54.79 ms | 55.95 | vs unmodified main same harness: 54.72 ms → no regression (the USAGE.md 44 ms reference was not reproduced by either arm in this environment) |
| Parity BF16 252, all tiers off | 63.77 ms | 64.25 | negative control |
| Opt-in FP4+FA4 252 | 28.39 ms | 28.67 | matches the reported 28.5 ms profile |
The five checklist items
-
Default fallback — PASS. Finite outputs, sanity PASS, prompt reuse OK;
opt-in env vars verified not to affect the default route; latency identical
to the unmodified-main baseline in a paired comparison. -
Opt-in profile — PASS. 28.39 ms p50 / 28.67 p95 (reported: median 27.7 /
p95 28.5). Rejections verified:use_fp8=True, parity=Trueand
parity=True, image_size≠252both raiseValueError. -
Prompt switching — PASS. A→B→A on one instance, no error; switch frame
performs one recapture (~2.3 s), subsequent frames back to ~28.4 ms; all
outputs finite. -
Finite outputs — PASS. 60-frame stress with three 4 s idle gaps:
0/60 non-finite; idle guard re-captures cleanly and latency returns to the
28–29 ms class immediately. -
Numerical comparison on real data — PASS. Real frames from the
fine-tuned SO-101 dataset (lerobot_so101_sim_v1_gr00t_n1d6_sim_fruits_cubes_10w,
new_embodimentslot), identical pixels/state/prompt and matched
denoise-noise seed on both arms:frame opt-in vs parity-BF16 cos maxd 50 0.999873 0.111 150 0.999893 0.089 300 0.999823 0.122 Null checks: each arm rerun against itself → cos 1.000000, so the protocol
and noise matching are exact. Consistent with the reported 0.999933 vs
HF eager. One methodology note: precision comparisons must be run on an
in-distribution checkpoint/inputs — out-of-distribution slots amplify
kernel-level numerical differences through the 4-step flow-matching and
grossly overstate quantization error.
Path-execution evidence
(fallback is numerically exact, so identical output alone proves nothing)
- Negative control: same parity frontend with all tiers off → 63.8 ms vs
28.4 ms opt-in (2.2×) — the FP4/FA4 tiers are doing the work. flash_rt.hardware.thor.fa4_backend.status()→ active; CuTe-DSL
compilation exercised during the opt-in run.- Per-tier isolation runs each engage their tier independently and stay at
cos ≥0.9998 vs parity on real data.
One environment note for users
The Isaac n1.6.1-release Eagle-Block2A-2B-v2/modeling_siglip2.py raises
NameError: Siglip2Model under transformers ≥5 (_init_weights references a
removed class). A patched copy (branches removed) works and was used here via
an explicitly pinned FLASHRT_N16_EAGLE_DIR. Worth a note in the doc or a
version pin for users on transformers 5.
Cleanup
No system changes left behind (PYTHONPATH-only isolation, editable install
untouched); worktrees kept at /mnt/FlashRT-pr177 and /mnt/FlashRT-main-bcec
for reference.
Verdict: all five pre-merge items pass on Thor hardware; ready to merge.
|
Thx!! |
Summary
GR00T N1.6-3B support for Jetson AGX Thor (SM110), including HF-alignment fixes, CUDA Graph lifecycle handling, and an explicit opt-in FA4/NVFP4 performance profile.
The existing public route remains the default:
use_fp8=True,image_size=224, andparity=False. The new HF-native parity path and approximate FP4/FA4 tiers do not activate unless requested.The reported opt-in profile measured 28.5 ms E2E on Thor (median 27.7 ms, p95 28.5 ms) with denormalized action cosine 0.999933 versus HF eager. These are the contributor's original Thor measurements; the audit follow-up below validated build and maintenance contracts but could not rerun Thor E2E while that machine was offline.
Audit follow-up
Commits
eb01fa8dand4c830e89close the merge blockers found during review:qk_norm_rope_rotate_half_bf16header and pybind symbol with the sameFLASHRT_HAVE_THOR_VLA_KERNELSdefinition as its SM100/SM110 source.CUTE_DSL_ARCH=sm_101aas a CuTe-DSL compilation alias without overridingFLASH_ATTENTION_ARCH; runtime FA4 dispatch now sees the physical SM110 capability and does not enter the SM100-only hd256 kernel.FLASHRT_N16_DIT_FP4,FLASHRT_N16_QWEN3_FP4,FLASHRT_N16_SIGLIP_FP4, andFLASHRT_N16_FA4opt-in.parity=True, and rejects the unsupported FP8-backbone/Torch-DiT hybrid configuration.concat2_bf16in DiT calibration, matching the production action/time embedding layout.FLASHRT_N16_EAGLE_DIR; no arbitrary Hugging Face cache revision is selected.Opt-in performance profile
Construct the parity frontend explicitly with the model's 252x252 evaluation resolution, then enable the measured tiers:
All four tiers independently fall back to BF16/Torch when disabled or unavailable.
FLASHRT_N16_DIT_STEPSremains 4; reducing diffusion steps is experimental and is not part of the reported profile.Validation
24 passedacross the N1.6 maintenance, prompt lifecycle, attention backend, calibration, and precision contract subset.compileall: passed.git diff --check: passed.flash_rt_kernels: full compile, link, and import passed; Thor-only binding absent.flash_rt_kernels: full compile, link, and import passed; Thor-only binding absent.flash_rt_kernelsandflash_rt_fp4: full cross-compile, link, and import passed; Thor-only binding present.Thor hardware was offline during this audit follow-up, so the updated commit still requires one final Thor E2E rerun covering default fallback, the opt-in profile, prompt switching, finite outputs, and HF numerical comparison before merge.
Scope
The runtime changes are limited to the GR00T N1.6 Thor frontend and its isolated Thor FA4 backend. No new N1.6 path is enabled for other models or architectures.