본문 바로가기
C.W.K.
Lesson 02 of 07 · published

디코드 상한: 대역폭 ÷ 토큰당 바이트

~16 min · llm-physics, decode-ceiling, bytes-per-token, safetensors, prediction, measured

Level 0스펙 시트 훑는 사람
0 XP0/91 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete
"모델이 디코드할 수 있는 최고 속도는 가중치를 초당 몇 번 읽을 수 있느냐야. 나머지 전부는 그보다 느린 이유고."

공식

디코드가 토큰마다 모든 활성 가중치를 한 번 읽어야 한다면, 초당 토큰은 초당 바이트 나누기 토큰당 바이트를 넘을 수 없어. 그게 공식 전부야. 상한 = 대역폭 ÷ 토큰당 바이트. 입력 둘은 앞 트랙들이 이미 만들어 놨어. 달성 메모리 대역폭(GPU 트랙)과 모델의 토큰당 바이트. 후자는 이 레슨이 조심스럽게 정의해. 이걸 틀리는 게 이 공식이 오용되는 제일 흔한 방식이거든.

어느 바이트가 세는가

실험 스크립트는 safetensors 헤더에서 모든 텐서의 크기를 읽고(진실은 모델 카드가 아니라 파일이야) 규칙 넷을 적용해. 비전 타워는 텍스트 디코드가 절대 안 읽어. Qwen3.5 체크포인트엔 하나 있고(9B에서 0.9 GB), 그걸 센 게 이 퀘스트의 첫 실수였어. 입력 임베딩은 표 조회야. 행렬 전체가 아니라 토큰당 한 줄. 체크포인트가 그걸 출력 헤드에 묶어 놨으면 예외로, 그땐 행렬이 헤드로서 통째로 읽혀. 전문가 혼합 레이어의 라우팅된 전문가는 토큰당 E 중 k개가 읽혀. 35B-A3B는 256 중 8을 읽으니 전문가 18 GB는 0.57로 세. 나머지 전부, 어텐션, 선형 어텐션, 밀집 MLP, 정규화, 헤드는 토큰당 한 번 읽혀.

모델 (4비트)디스크비전임베딩전문가 (k/E)토큰당 바이트증거
Qwen3.5-0.8B0.625 GB0.2010.143, 묶임 (셈)—0.424 GB헤더에서 나온 물리
Qwen3.5-9B5.950.9120.572, 안 묶임 (제외)—4.466물리
Qwen3.5-27B16.050.9210.715, 안 묶임 (제외)—14.42물리
Qwen3.5-35B-A3B20.390.8930.286, 안 묶임18.12, 8/2561.659물리
Llama-3.2-1B0.695—0.148, 묶임 (셈)—0.695물리

예측 대 측정

GPU 트랙의 달성 대역폭을 넣으면 office에서 상한과 실험의 실측 디코드 속도가 이렇게 줄 서.

모델상한 (638 GB/s ÷ 바이트)실측 (office)실측 ÷ 상한증거
Qwen3.5-0.8B1,505 tok/s33822%실측 2026-09-15
Qwen3.5-2B60225743%실측
Qwen3.5-4B26914855%실측
Qwen3.5-9B1439567%실측
Qwen3.5-27B4432.674%실측
Qwen3.5-35B-A3B3858923%실측

상한은 한 번도 안 넘겨. 누가 보여주는 어떤 디코드 숫자든 첫 번째 검사가 이거야. 그리고 모델이 상한에 닿는 비율은 모델 크기와 함께 올라가. 제일 작은 건 22%, 제일 큰 밀집 모델은 74%. 이유는 바이트에 비례하지 않는 토큰당 고정 비용이야. 레이어마다 커널 띄우기, 샘플링, Python 루프. 실험 트랙이 직접 적합했어. office에서 토큰당 1.7밀리초쯤 더하기 바이트 ÷ 497 GB/s. 1.7밀리초는 0.8B 토큰의 대부분이고 27B 토큰의 10분의 1이야. 전문가 혼합 줄은 전문가 레슨이 설명하는 예외야. 바이트는 작은데 커널 수가 많아.

그러니 진짜 기계에 대한 공식의 정직한 형태는 토큰당 초 = 오버헤드 + 토큰당 바이트 ÷ 실효 대역폭이고, 두 상수는 기계마다, 런타임마다 재. 상한은 오버헤드가 0으로 가고 실효 대역폭이 스트리밍 수치로 갈 때의 극한이야. 다운로드할 수 있는 어떤 것도 첫 항을 못 바꿔. 둘째 항을 바꾸는 건 대역폭이나 더 적은 바이트뿐이야.

Code

bytes_per_token.py — safetensors 헤더에서 하는 회계·python
#!/usr/bin/env python3
"""Bytes a token must read during decode, from the checkpoint's own headers.
Rules: skip the vision tower; skip the embedding table unless tied to the
head; count routed experts at k/E; count everything else once."""
import json, struct, sys
from pathlib import Path
from huggingface_hub import snapshot_download

repo = sys.argv[1] if len(sys.argv) > 1 else "mlx-community/Qwen3.5-9B-4bit"
path = Path(snapshot_download(repo, local_files_only=True))
cfg = json.loads((path / "config.json").read_text()); tc = cfg.get("text_config", cfg)
E = tc.get("num_experts") or tc.get("num_local_experts") or 0
k = tc.get("num_experts_per_tok") or 0
tied = bool(cfg.get("tie_word_embeddings", tc.get("tie_word_embeddings", False)))

total = vision = embed = expert = 0
for f in sorted(path.glob("*.safetensors")):
    with f.open("rb") as fh:
        header = json.loads(fh.read(struct.unpack("<Q", fh.read(8))[0]))
    for name, meta in header.items():
        if name == "__metadata__":
            continue
        size = meta["data_offsets"][1] - meta["data_offsets"][0]
        total += size
        if "vision" in name or "visual" in name:
            vision += size
        elif "embed_tokens" in name:
            embed += size
        elif "switch_mlp" in name or ".experts." in name:
            expert += size

per_token = total - vision - (0 if tied else embed)
if E:
    per_token -= expert * (1 - k / E)
print(f"{repo}: disk {total/1e9:.3f} GB, vision {vision/1e9:.3f}, embed {embed/1e9:.3f} ({'tied' if tied else 'untied'}), "
      f"experts {expert/1e9:.3f} at {k}/{E} -> {per_token/1e9:.3f} GB per token")
for bw in (638, 391, 740, 97):
    print(f"   ceiling at {bw} GB/s: {bw*1e9/per_token:7.1f} tok/s")

External links

Exercise

다운로드해 둔 체크포인트(아무 mlx-community 4비트 모델)에 bytes_per_token.py를 돌리고, 모델을 돌리기 전에 네 맥의 달성 대역폭 기준 상한을 카드에 적어. 그다음 mlx-lm으로 200토큰을 디코드해서 실측 속도를 기록해. 실측 ÷ 상한을 계산하고 office 표와 비교해. 네 비율이 모델 크기가 예측하는 자리에 앉아?
Hint
작은 모델은 낮은 비율, 큰 모델은 높은 비율. 비슷한 크기의 office 줄보다 네 비율이 훨씬 높으면 네 런타임이 Ultra 위의 mlx-lm 0.31보다 토큰당 오버헤드가 적은 거야. 가능한 일이고, 버전이랑 같이 적어둘 만해. 100%를 넘으면 네 토큰당 바이트가 틀린 거야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인 — 댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.