Skip to content

NVIDIA FP4-Local checkpoints on DGX Spark (TP=1, TP=2): format, model and speed plan in rounds #565

Open

Description

@juliankang4

개요

이 이슈는 따로 관리하던 체크리스트 이슈 세 개(#493, #494, #495)를 하나로 합친 플랜입니다. 기존 플랜을 3개 이슈로 나눠놨더니 제가 라운드 처리하기가 힘들어서 통합했습니다!!

전체 작업은 NVIDIA가 내놓은 FP4-Local 체크포인트를 bloomery가 형식 그대로 읽어 DGX Spark에서 빠르게 돌리는 일입니다.
1노드에서는 Qwen3.8-Flash-Next-FP4-Local을 돌리고 2노드에서는 GLM-5.3-Flash-FP4-Local을 tensor parallel(TP=2)로 돌립니다.

크게 3라운드로 나눠놨습니당.

  • 1라운드는 베이스를 닦고 있습니다. 여기에 가장 많은 시간을 부으려고 합니당. 지금 다루는 두 모델뿐 아니라 앞으로 나올 모델도 그대로 올라탈 수 있게 가중치 형식과 공용 층을 먼저 깔고 있습니다.
    형식마다 읽기와 커널, TP 분할 단위를 한 번만 만들고 모델은 텐서 이름과 역할, shape만 알려 줍니다.

  • 2라운드에서는 이 기반 위에 Qwen3.8과 GLM의 FP4-Local 모델을 올리고 두 모델의 MTP draft도 함께 붙입니다.

  • 3라운드는 속도 최적화입니다. 2라운드에서 잰 수치를 기준으로 삼아 PR마다 main과 A/B로 비교합니다.

라운드 안의 PR이 모두 머지되면 main을 한 번 재고 그 라운드를 닫습니다.
이때 잰 수치가 다음 라운드의 기준이 됩니다.

형식과 공용 층을 먼저 깔아 두면 모델이 바뀌어도 형식 코드를 다시 만들 필요가 없습니다.
새 모델을 붙일 때 손댈 곳은 모델의 이름 표와 shape 정도로 줄어듭니다.
라운드가 끝날 때마다 main 수치를 이 이슈에 댓글로 남기니 단계마다 무엇이 얼마나 달라졌는지도 수치로 바로 확인할 수 있습니다.

라운드와 함께 두 갈래를 따로 둡니다. 7절 최적화는 체크포인트 폴더 하나만 받아 flag 없이 띄워도 가장 빠른 설정으로 뜨게 하는 일이고, 8절 사용성은 설치, 튜닝, 오류 안내를 다룹니다.

1. Goal

bloomery serves NVIDIA's FP4-Local checkpoints on DGX Spark with every part in the format the checkpoint ships, and the output matches a pinned reference:

  • One DGX Spark (TP=1): nvidia/Qwen3.8-Flash-Next-FP4-Local, revision b9ce18a6.
  • Two DGX Sparks (TP=2, tensors split across both nodes): nvidia/GLM-5.3-Flash-FP4-Local, revision 76348ddb.

There is no speed target. Each round is measured against the round before it, and section 2 gives the numbers this work starts from.

This issue replaces #493, #494 and #495. Items are ticked as they finish, and code items link their pull request.

2. Starting point on main

These numbers were measured on main before this work started.

One DGX Spark, main eb1b521f, Qwen3.8-Flash-Next from unsloth UD-Q4_K_XL 766911a6, one request, draft off, median of three rounds. Each cell is decode / prefill in tok/s:

Prompt tokens Code as functions JavaScript English prose Korean prose
4,096 28.96 / 993.7 28.15 / 1,029.0 28.58 / 1,023.5 29.04 / 1,009.1
16,384 28.73 / 941.5 28.35 / 964.7 28.35 / 958.3 28.79 / 963.4
32,768 28.98 / 918.8 28.07 / 931.4 28.08 / 942.0 28.71 / 939.5
65,536 28.88 / 888.0 27.83 / 890.4 27.55 / 911.4 28.43 / 910.0
131,072 24.02 / 828.8 26.83 / 829.9 27.13 / 844.6 28.06 / 851.5
  • MTP with the shared Q8_0 draft, 1,024 tokens a request, against draft off [derived]: 1.52 to 1.66 times on code, 1.18 to 1.28 times on greedy English prose.
  • A 4-row MTP verify costs 68 to 70 ms against a 33.6 ms plain step, 2.0 to 2.1 times [derived].
  • The upload phase of loading takes 29.05 to 30.52 s. The drive reads 10.034 GB/s with eight direct readers.

Two DGX Sparks, main 7de31349 on the expert tier, where node 0 runs all of the attention alone, GLM-5.3-Flash from unsloth UD-Q4_K_XL a38483c8, prompt fed as steps, one request, 128 decoded tokens, draft off. With one observation each, this is a reference point, not a comparison:

Measure Code as functions, 4,096 English prose, 4,096 Code as functions, 16,256
Decode tok/s 2.932 3.292 2.288
Prefill tok/s 2.676 3.346 2.350
Time to first token, s 1,530.9 1,224.3 6,917.8
Load, s 202 199 190

Current main cannot open either FP4-Local folder. Until a round serves a checkpoint on main, its speed is compared with these GGUF numbers and labelled a comparison across files.

3. How results are measured

Each round's main numbers are posted as a comment on this issue. They are medians and percentiles over 20 requests a row. ISL/OSL is input and output tokens a request and C the requests in flight at once. Output tok/s is the total over all requests, output tok/s/user one request's rate and prefill tok/s one request's prompt rate. In the Text column, code is the Python function text, en the English prose and ko the Korean prose. A value in parentheses is the change in percent against the previous round, or against section 2 in round 1. Load time is reported apart.

A speed pull request names, before any code, the metrics it should improve and the metrics it must not worsen. Main and the head then run alternately for three rounds in one lease. A metric is better when all three paired rounds favour the head and the median gap is larger than main's own spread over its three runs. The change stays when a listed metric is better, none is worse and the greedy tokens match main; otherwise the code is dropped and the measurement stays on record. Fewer bytes moved, fewer kernel launches, less memory or less CPU time at the same tok/s also counts.

Prompts come from four pinned public texts: code as functions (Python), JavaScript, English prose and Korean prose. A pull request uses the function code and the English prose, and all four when it changes MTP, the tokenizer or prompt handling.

4. Round 1: TP=1

Weight formats and shared layers that any model uses, and Qwen3.8-Flash-Next on one DGX Spark (TP=1) from NVIDIA's NVFP4 and FP4-Local checkpoints, with its MTP drafts and its speed work. Each format's reader, kernels, batched paths and TP cut unit are written once; a model supplies only tensor names, roles and shapes. Speed work targets FP4-Local's prompt processing, time to first token, decode and MTP decode. NVFP4 follows through the routed expert, GDN, attention, hyper-connection, scheduler and concurrent-request paths.

Remaining Round 1 items

5. Round 2: TP=2

GLM-5.3-Flash-FP4-Local on two DGX Sparks (TP=2) and its MTP layer, on the round 1 base.

  • GLM card paths for the FP4-Local formats: NVFP4 experts, MXFP8, FP8 block and BF16 projections
  • GLM-5.3-Flash-FP4-Local loaded on TP=2 from the folder and checked against its reference
  • TP=2 decode on two DGX Sparks
  • TP=2 prompt batches
  • The GLM server on TP=2
  • GLM beyond 16,384 positions: run the full context on two Sparks
  • NextN (MTP) layer of the GLM FP4-Local checkpoint on TP=2, MTP on against MTP off

6. Round 3: optimization and speed

Optimization against the round 2 numbers, one pull request at a time, each with its own A/B under section 3. The round 2 measurement sets the order. Candidates:

  • GLM verify past two rows
  • MXFP4 routed prompt GEMM on the GB10's block-scaled tensor cores
  • GLM FP4-Local's BF16 projections written as Q8_0 per rank slice, as an opt-in lever with its measured KL; the default stays BF16
  • Early send of TP partial rows: start the network send while the producing GEMM still runs
  • TP=2 gather wait in one shot on the GB10
  • L2 prefetch of the next weights while a gather waits
  • Prompt-size gathers striped over both ports of the cable
  • Prompt cache on TP=2, each node saving its own half of the state

7. Optimization: the default setup

One folder and one command: bloomery-serve -m <FP4-Local folder> starts the fastest setup the checkpoint allows, with nothing else to download. A flag the user gives always wins, and the server says at start what it chose.

  • Qwen3.8 FP4-Local starts at its fastest from the folder alone: the checkpoint's own MTP draft runs at least as fast as the separate Q8_0 GGUF draft, the draft is opened once at load, a context that fits is chosen when --ctx-size is not given, drafting is on where it is faster, and a startup line names each choice and the flag that changes it (qwen38: a checkpoint folder's plan holds its load's least part reads #898)
  • A q8_0 KV cache for Qwen3.8 with llama-server's --cache-type-k q8_0 meaning, chosen by the FP4-Local setup when no flag is given, behind an accuracy gate

8. Usability

A DGX Spark user installs bloomery and runs a model without building it, tunes it with the flags llama-server users know, and gets an error that names the cause.

9. Rules and related work

Round rules

PRs inside a round merge in a fixed order, and PRs that share files rebase once onto the one before. A round ends when every PR in it is merged and main is measured once. That measurement is the next round's reference, so the next round starts after it.

Speed PRs merged outside the rounds

Accuracy and related work

  • Formats: every decoder is compared byte for byte with a pinned Python reference written from the format's published reference code, and a structural error is refused by name.
  • Models: the reference is a Hugging Face Transformers forward of each checkpoint in f32, with every weight dequantized by its own rule and input_scale ignored. The served model is judged by the existing numerical gates with unchanged bands.
  • TP: all ranks are bit-equal at every step, and the TP body run on one node matches the existing body bit for bit.
  • A pull request that changes shared code shows with a test that the existing card, host and NVMe paths behave as before.

10. Other pull requests from this work

Fixes and tool changes that came up while doing the work above. None of them is a plan item, and each one stands on its own.

Engine:

Docs:

Gates and tools:

AI help

AI 코딩 에이전트가 문서를 작성한 후 이슈를 올렸습니다. 문서 리뷰와 이슈 리뷰는 제가 직접 했습니다.

Activity

  1. added
    hardwarebloomery on a card, CPU or machine we have not run
    dgx-sparkThe DGX Spark (aarch64, GB10) port
    gpuThe card engine: crates/gpu*, crates/app
    on Oct 20, 2026
  2. changed the title [-]NVIDIA FP4-Local checkpoints on one, two and four DGX Sparks (TP=1, TP=2, TP=4): format, model and speed plan in rounds[/-] [+]NVIDIA FP4-Local checkpoints on DGX Spark (TP=1, TP=2, TP=4): format, model and speed plan in rounds[/+] on Oct 20, 2026
  3. added
    modelThe CPU engine and model files: crates/model, models, hf, gguf, tokenizer, jinja
    placementPlacement planning: crates/placement
    trackingA status issue kept open and updated as the work moves
    on Oct 20, 2026
  4. midagedev commented on Oct 20, 2026

    @midagedev
    Owner

    세 이슈를 하나로 합쳐 주셔서 따라가기 훨씬 편해졌습니다. 감사합니다!

    포맷과 공용 층을 먼저 한 번만 깔고 모델은 이름과 shape만 넘기는 구조는 저희가 가려는 방향과 그대로 맞습니다. 측정 조건(commit, revision, 프롬프트, 횟수)과 [derived] 표기, 정확도 기준(고정 참조, band 유지, rank 비트 동일)도 좋습니다.

    지금 빠르게 진행 중이시니 계획대로 먼저 진행해 주세요. 아래 내용은 진행을 막지 않습니다. 괜찮으시면 후속 정리는 제가 따로 하겠습니다. 다만 저는 Spark가 없어서, Spark에서 재야 하는 일은 그쪽 몫으로 남겨 둡니다.

    • A/B 판정: AGENTS.md는 승수(3/3)가 아니라 효과 크기와 구간(tools/ref/card.py), 그리고 PR마다 미리 적는 예측값과 band를 기준으로 합니다. 3/3은 효과가 없어도 1/8 확률로 나옵니다. Spark의 ruler는 #13의 A/A가 이미 있어서, 이 기준을 docs/contrib/dgx-spark.md §4에 적어 두었습니다(c3b7100). 측정은 Spark가 있는 쪽에서 해 주셔야 해서, 다음 속도 PR부터 카드의 sd:에 그 A/A를 적고 효과와 구간으로 보고해 주시면 됩니다.
    • prefill 열: 헤드라인 규칙이 P = 512, 4096이라서 라운드 표에 이 두 열을 더해 두었습니다. 값은 라운드를 닫을 때 Spark에서 함께 재 주세요.
    • 카드 타입 표 (1라운드): crates/gguf/src/qtype.rs의 QROWS가 타입당 한 행을 이미 들고 있습니다. placement에 새 표를 두기보다 이 행에 카드 쪽 열을 붙이는 방향이 어떨까 싶습니다. 먼저 만드시면 제가 이어 붙여도 됩니다.
    • BF16 as shipped: 저희도 BF16 native를 계획에 넣어 두었는데, 여기서 먼저 하시면 저희 쪽은 접겠습니다.
    • gpu: gemm_nvfp4p — the NVFP4 planes in the 32-value GEMM #344 (gemm_nvfp4p): 내용이 이미 main에 있어서 닫았습니다.
    • 4라운드 (V4.1 경로 이동): 3090/A6000 경로에 걸리므로 그때 ptx-scan 동일성과 소유 게이트를 저희 카드에서 대신 돌려 드리겠습니다.

    (Claude Code로 검토하고 정리한 코멘트입니다.)

  5. juliankang4 commented on Oct 20, 2026

    @juliankang4
    CollaboratorAuthor

    확인했습니다. 말씀하신 방향대로 맞추겠습니다.

    • Card type table: it goes into the existing QROWS rows in crates/gguf/src/qtype.rs as card columns, one row a type, with no new table in placement.
    • A/B: the round measurements will report the effect size and interval from tools/ref/card.py against a prediction and band written in each PR before it runs, instead of a 3/3 win count.
    • BF16 as shipped, gemm_nvfp4p and the round 4 ptx-scan on the maintainer's cards: understood, and thanks for taking these.

    AI help

    AI 코딩 에이전트가 문서를 작성한 후 댓글을 올렸습니다. 문서 리뷰와 댓글 리뷰는 제가 직접 했습니다.

  6. midagedev commented on Oct 21, 2026

    @midagedev
    Owner

    참고 정보 하나 공유드립니다. 진행을 막는 내용은 아닙니다.

    • NVIDIA의 두 FP4-Local 모델 카드는 Spark와 RTX PRO 6000 레시피 모두에 VLLM_B12X_MOE_FP4_FORCE_A16=1을 넣습니다. routed expert를 NVFP4 가중치와 BF16 activation(W4A16)으로 돌린다는 뜻입니다.
    • 두 저장소의 현재 revision은 이 이슈가 고정한 값과 같습니다(Qwen3.8 b9ce18a6, GLM 76348ddb).
    • 카드의 정확도 표(BF16 대비 FP4-Local: Qwen3.8 GPQA 92.2 → 91.5 %, GLM ToolEvalBench 91.9 → 92.4 %)는 NVIDIA가 잰 값이라 band의 외부 참고로만 보시면 됩니다.

    (Claude Code로 정리한 내용입니다.)

  7. juliankang4 commented on Oct 21, 2026

    @juliankang4
    CollaboratorAuthor

    공유 감사합니다. 2라운드 기준에 반영하겠습니다.

    • The round 2 FP4-Local PRs will state the activation form of each arm and hold the accuracy band against a W4A16 reference, the form the model cards run.
    • NVIDIA's GPQA and ToolEvalBench numbers stay an outside reference only, as suggested.

    AI help

    AI 코딩 에이전트가 문서를 작성한 후 댓글을 올렸습니다. 문서 리뷰와 댓글 리뷰는 제가 직접 했습니다.

  8. changed the title [-]NVIDIA FP4-Local checkpoints on DGX Spark (TP=1, TP=2, TP=4): format, model and speed plan in rounds[/-] [+]NVIDIA FP4-Local checkpoints on DGX Spark (TP=1, TP=2): format, model and speed plan in rounds[/+] on Oct 21, 2026
  9. juliankang4 commented on Oct 25, 2026

    @juliankang4
    CollaboratorAuthor

    지금부터 블루머리의 스파크 작업은 FP4-Local 양자화에 치중하려고 합니다. NVFP4 체크포인트의 속도 작업을 어떻게 할지도 정해서 알려드립니다.

    현재 main(7f3efbf)에서 Qwen3.8을 DGX Spark 한 대(TP=1)로 돌린 값입니다. 4K code 프롬프트를 요청 하나씩 보냈고 MTP draft는 껐습니다.

    FP4-Local NVFP4 차이
    decode (tok/s) 30.18 22.56 FP4-Local이 34% 빠름
    prefill (tok/s) 2,053.8 1,954.0 FP4-Local이 5% 빠름
    TTFT p95 (ms) 2,000.5 2,102.5 FP4-Local이 102 ms 짧음

    routed expert는 두 체크포인트가 같습니다. FP4-Local은 expert scale을 압축해서 저장하지만 로드할 때 풉니다. 카드에 올라간 뒤에는 NVFP4 체크포인트와 같은 NVFP4 weight와 E4M3 scale이 되고 같은 kernel을 탑니다.

    속도 차이는 expert 밖의 큰 행렬에서 납니다. GDN projection, full attention과 indexer, shared expert를 FP4-Local은 MXFP8(원소당 약 1 B)로 담고, NVFP4 체크포인트는 BF16(원소당 2 B)으로 담습니다. decode는 토큰마다 이 행렬을 거의 다 읽기 때문에 weight를 얼마나 읽느냐가 속도를 정합니다. 토큰 하나에 읽는 양을 계산하면 이렇습니다.

    행렬 FP4-Local (GB/토큰) NVFP4 (GB/토큰)
    GDN qkv, z, out (36층) 2.14 4.15
    full attention, indexer (12층) 0.64 1.23
    shared expert (48층) 0.24 0.47
    routed expert (층마다 10개) 1.33 1.33

    NVFP4 체크포인트가 토큰마다 약 2.9 GB를 더 읽습니다. GB10에서 실제로 나오는 읽기 속도(약 230 GB/s)로 나누면 토큰당 약 12 ms입니다. 측정한 decode 시간 차이(토큰당 33.1 ms와 44.3 ms, 약 11 ms)와 크기가 맞습니다. prefill은 한 번 읽은 weight를 여러 토큰에 다시 쓰므로 이 차이가 덜 드러납니다.

    그래서 NVFP4 체크포인트에만 해당하는 속도 작업은 여기서 멈추려고 합니다.

    • decode 차이는 체크포인트가 dense 행렬을 BF16으로 담은 데서 옵니다. 체크포인트가 고른 BF16 정밀도를 그대로 쓰는 한 엔진에서 줄일 수 있는 몫이 아닙니다.
    • NVFP4에만 있는 경로에서 찾은 개선 가운데 가장 큰 것은 GDN β/α 행렬을 F32로 넓히지 않고 BF16 그대로 읽는 것입니다. decode에서 약 0.17%입니다.
    • routed expert, GDN, attention, scheduler, 동시 요청 처리는 두 체크포인트가 같은 코드를 씁니다. FP4-Local을 위한 개선이 NVFP4에도 그대로 들어갑니다.

    정확도는 이번에 비교하지 않았습니다. dense 행렬을 BF16으로 담은 NVFP4 체크포인트가 정확도에서 나을 수 있으니 NVFP4 지원은 지금처럼 유지합니다.

    이후에는 이렇게 하겠습니다.

    • NVFP4 prefill에서 BF16 dense 행렬이 차지하는 시간을 한 번 재 보고, 한 가지 변경으로 prefill이 1% 넘게 빨라질 때만 따로 작업합니다.
    • PR마다 NVFP4 숫자도 함께 재서 본문에 적습니다. 속도 판단은 FP4-Local 결과로 합니다.
  10. juliankang4 commented on Oct 27, 2026

    @juliankang4
    CollaboratorAuthor

    1라운드 중간 측정값을 공유합니다. bloomery 수치만 적었습니다.

    main cb8d92e에서 Qwen3.8을 DGX Spark 한 대(TP=1)로 돌렸습니다.

    • 모델: nvidia/Qwen3.8-Flash-Next-FP4-Local(b9ce18a). MTP draft는 unsloth/Qwen3.8-Flash-Next-GGUF의 MTP/mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf(766911a)입니다.
    • 요청: 128 토큰을 생성합니다. greedy(temperature 0, top_k 1)이고 cache_prompt false입니다. 글은 코드와 영어 산문 두 가지입니다.
    • 서버: 동시 요청 수마다 서버를 하나씩 띄웠습니다(--parallel 1 --ctx-size 33024, --parallel 2 --ctx-size 16896, --parallel 4 --ctx-size 17408).
    • 값: 세 라운드의 중앙값입니다.
    • MTP off와 on은 같은 종류의 Spark 두 대에서 따로 쟀습니다. 같은 설정을 두 대에서 재 보면 MTP off는 모든 칸이 1.5% 안에서 같았습니다.

    decode는 요청별 1000 / TPOT의 중앙값이고, prefill은 prompt 토큰 수 / TTFT의 중앙값입니다. total은 그 칸에서 생성한 토큰 전체를 첫 요청의 첫 토큰부터 마지막 토큰까지의 시간으로 나눈 값입니다. 속도 단위는 tok/s입니다.

    요청 하나씩

    prompt 글 decode off decode on TTFT p50 off (ms) TTFT p50 on (ms) prefill off (tok/s)
    4K 코드 30.4 55.7 1,916 2,003 2,135
    4K 영어 산문 30.1 39.1 1,881 1,967 2,176
    8K 코드 29.9 57.1 3,878 3,999 2,112
    8K 영어 산문 29.9 38.2 3,750 3,882 2,185
    16K 코드 29.9 58.7 7,829 8,106 2,093
    16K 영어 산문 29.9 37.0 7,566 7,927 2,165
    32K 코드 29.5 49.6 16,069 16,629 2,039
    32K 영어 산문 29.7 37.2 15,395 16,020 2,129

    동시 요청 (4K prompt)

    동시 요청 글 total off total on 요청별 decode off 요청별 decode on TTFT p50 off (ms) TTFT p50 on (ms)
    2개 코드 30.2 34.5 21.0 23.8 1,940 2,025
    2개 영어 산문 30.7 31.7 21.1 21.1 1,863 1,928
    4개 코드 35.8 37.8 12.3 11.8 3,831 2,002
    4개 영어 산문 36.6 36.4 12.6 11.5 3,763 1,948
    • MTP on에서 4K 요청을 하나씩 보낸 칸(코드와 산문 합)은 verify 한 번마다 draft 토큰을 평균 2.18개 내고, 그 가운데 58%가 받아들여졌습니다.
    • MTP on과 off의 답은 비교한 요청 205개 모두에서 글자까지 같았습니다.

    1라운드 PR이 들어간 뒤 같은 방법으로 다시 재서 올리겠습니다.

    (AI 코딩 에이전트가 작성한 코멘트이며, 내용은 제가 확인했습니다.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    dgx-sparkThe DGX Spark (aarch64, GB10) portgpuThe card engine: crates/gpu*, crates/apphardwarebloomery on a card, CPU or machine we have not runmodelThe CPU engine and model files: crates/model, models, hf, gguf, tokenizer, jinjaplacementPlacement planning: crates/placementtrackingA status issue kept open and updated as the work moves

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions