본문 바로가기
C.W.K.
Lesson 01 of 06 · published

숫자보다 먼저 예측을 적어

~14 min · lab, protocol, prediction, safetensors, provenance, measured

Level 0스펙 시트 훑는 사람
0 XP0/91 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete
"예측하지 않은 숫자는 놀랄 수 없는 숫자야. 실험실의 첫 규칙은 놀람을 가능하게 만드는 거야."

프로토콜

이 퀘스트의 모든 실측 숫자는 작은 프로그램 하나, 이 퀘스트를 위해 쓴 silicon_lab.py에서 나왔어. 하루에 이 집의 맥 네 대에서 돌렸어. Air의 기본 M3, 2023 MacBook Pro의 M3 Max, office Studio의 M3 Ultra, music Studio의 M2 Ultra. 다섯 가지를 정해진 순서로 하고, 그 순서가 프로토콜이야. 스탬프: 뭘 돌리기 전에 기계를 역할 별칭으로, 칩을 MLX가 부르는 이름으로, 메모리와 권장 작업 집합, macOS 빌드, MLX와 mlx-lm 버전, UTC 시각을 기록해. 이게 없는 숫자는 소문이야. 바이트 세기: 체크포인트의 safetensors 헤더를 열어 디코드 단계가 실제로 읽는 걸 물리 트랙의 규칙으로 더해. 비전 타워는 빼고, 출력 헤드에 묶이지 않았으면 임베딩 테이블도 빼고, 라우팅되는 전문가는 E 중 k로. config 파일은 모델이 뭔지 말하고, 헤더는 디스크에 뭐가 있는지 말해. 예측: 칩의 스펙 대역폭을 그 바이트로 나눠서 모델을 로드하기 전에 상한을 출력해. 워밍업: 토큰 여덟 개를 생성하고 버려. 여정 트랙의 첫 순전파 페이지 건드리기가 타이밍에 안 들어가도록. 측정: 209토큰쯤의 고정 프롬프트, 그리디로 200토큰 출력, 배치 1, 그리고 프리필 속도, 첫 토큰까지의 시간, 디코드 속도, 피크 메모리를 예측 옆에 기록해.

예측이 먼저인 이유

첫 office 사다리는 틀렸고, 예측이 그걸 잡은 방법이야. 그 실행은 디스크의 모든 바이트를 토큰당 읽는 바이트로 셌어. 0.8B에 0.42 대신 0.63 GB, 9B에 4.47 대신 5.95, 혼합 모델에 1.66 대신 20.39. 잰 디코드 속도는 멀쩡했어. 거기서 유도한 실효 대역폭이 아니었어. 버스가 819를 나르는 칩에서 혼합 모델에 1,815 GB/s. 상한 위의 숫자는 빠른 기계가 아니라 틀린 집계야. 그럴듯해 보이는 속도들의 화면에서 그 줄이 튀어나온 유일한 이유는 상한이 옆에 출력돼 있었기 때문이야. 실행 1은 버렸고, 위의 바이트 규칙을 썼고, 물리 트랙의 전문가 레슨이 그 고백을 실어. 예측이 없었다면 틀린 숫자가 결과였을 거야.

측정 기록엔 뭐가 들어가나

실행당 한 줄, JSON, 기계가 보관하는 파일에 덧붙여. 실험실의 기록은 스탬프, 바이트 집계(전체, 비전, 임베딩, 전문가, 묶임 여부, 토큰당), config가 말하는 양자화, 프롬프트와 생성 토큰 수, 속도 넷, 피크 메모리, 그리고 실효 대역폭을 옆에 둔 예측을 실어. 이 퀘스트의 모든 표는 그 줄들에서 네가 같은 파일에 돌릴 수 있는 스크립트로 만들었고, 퀘스트가 보관하는 측정 동반 문서는 그것들의 사람이 읽는 색인이야. 네가 채워 온 맥 카드는 같은 아이디어를 손으로 한 거야. 별칭, 날짜, 빌드, 버전, 모델, 비트, 토큰당 바이트, 예측, 측정, 그리고 격차. 아래 표는 벤더의 819 GB/s로 예측해. 물리 트랙의 같은 행들은 office에서 커널이 실제로 스트림한 638 GB/s로 예측했고, 그래서 거기 비율이 더 높게 읽혀(양 끝에서 22%와 74%). 같은 측정, 정직한 분모 둘. 카드는 어느 걸 썼는지 말해야 해.

office, 2026-09-15, mlx 0.32.2토큰당 바이트예측 상한(스펙 819 GB/s)실측 디코드스펙 상한 대비증거
Qwen3.5-0.8B-4bit0.42 GB1,932 tok/s338.218%실측
Qwen3.5-9B-4bit4.47 GB18395.152%실측
Qwen3.5-27B-4bit14.42 GB56.832.657%실측
Qwen3.5-35B-A3B-4bit(실행 1, 틀린 집계)20.39 GB40.289.0221% — 불가능실측, 폐기
Qwen3.5-35B-A3B-4bit(수정)1.66 GB49489.118%실측

Code

silicon_lab.py(발췌) — 스탬프, 헤더에서 바이트 세기, 예측, 그다음 실행·python
def stamp(alias: str) -> dict:
    """Who, what, when: a measurement without this is a rumour."""
    info = mx.device_info()
    return {
        "alias": alias,
        "device": info.get("device_name"),
        "memory_gb": round(info.get("memory_size", 0) / 1e9, 1),
        "recommended_working_set_gb": round(info.get("max_recommended_working_set_size", 0) / 1e9, 1),
        "macos": macos_build(),
        "mlx": mx.__version__,
        "mlx_lm": mlx_lm.__version__,
        "date": datetime.now(timezone.utc).isoformat(timespec="seconds"),
    }


def weight_bytes(path: Path) -> dict:
    """Bytes a token must read during decode — from the safetensors headers, not the config.
    Not every byte on disk is read per token: a vision tower never is; the input embedding is a
    one-row lookup unless tied to the output head; a mixture reads k of E routed experts."""
    cfg = json.loads((path / "config.json").read_text())
    text_cfg = cfg.get("text_config", cfg)
    n_experts = text_cfg.get("num_experts") or text_cfg.get("num_local_experts") or 0
    k = text_cfg.get("num_experts_per_tok") or 0
    tied = bool(cfg.get("tie_word_embeddings", text_cfg.get("tie_word_embeddings", False)))
    total = vision = embed = expert = 0
    for f in sorted(path.glob("*.safetensors")):
        with f.open("rb") as fh:
            n = struct.unpack("<Q", fh.read(8))[0]
            header = json.loads(fh.read(n))
        for name, meta in header.items():
            if name == "__metadata__":
                continue
            a, b = meta["data_offsets"]
            size = b - a
            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
    touched = total - vision - (0 if tied else embed)
    if n_experts:
        touched -= expert * (1 - k / n_experts)
    return {"weight_bytes_total": total, "weight_bytes_vision": vision, "weight_bytes_embed": embed,
            "weight_bytes_experts": expert, "tied_embeddings": tied, "experts": n_experts,
            "experts_per_token": k, "weight_bytes_per_token": int(touched)}


def ladder(args) -> None:
    base = stamp(args.alias)
    for repo in args.models:
        path = snapshot_dir(repo)
        wb = weight_bytes(path)
        predicted = args.spec_gbps * 1e9 / wb["weight_bytes_per_token"]          # THE PREDICTION, before load()
        print(f"prediction: {args.spec_gbps} GB/s / {wb['weight_bytes_per_token']/1e9:.2f} GB = {predicted:.1f} tok/s ceiling")
        model, tokenizer = load(str(path))
        prompt_ids = build_prompt(tokenizer, args.prompt_tokens, QUESTION)
        run_once(model, tokenizer, prompt_ids, 8)                                  # warm the kernels; discard
        rec = {**base, "experiment": "ladder", "model": repo, **wb}
        rec.update(run_once(model, tokenizer, prompt_ids, args.max_tokens))        # prefill, TTFT, decode, peak
        rec["predicted_decode_tps"] = round(predicted, 1)
        rec["effective_gbps"] = round(rec["generation_tps"] * wb["weight_bytes_per_token"] / 1e9, 1)
        with Path(args.out).open("a") as fh:
            fh.write(json.dumps(rec) + "\n")                                        # one JSON line per run; the file is the record

# office, run 1 (every byte on disk counted per token), 2026-09-15:
#   Qwen3.5-35B-A3B-4bit  20.39 GB/tok  predicted 40.2  measured 88.99  -> effective 1814.6 GB/s on an 819 GB/s bus
# corrected: 1.66 GB/tok, predicted 493.7, measured 89.1, effective 147.9 GB/s

External links

Exercise

뭘 돌리기 전에: 네 캐시의 4비트 모델 하나에 대해 safetensors 헤더를 열고, 바이트 규칙 셋을 적용하고, 네 맥의 상한(스펙 대역폭 ÷ 토큰당 바이트)을 카드에 적어. 그다음에야 그 모델 하나로 실험실의 사다리를 돌리고 실측 디코드를 옆에 적어. 비율이 100% 위면 다른 무엇보다 먼저 집계 오류를 찾아.
Hint
흔한 집계 오류, 빈도순: 비전 타워(작은 VLM 바이트의 5분의 1), 묶이지 않은 모델의 임베딩 테이블, 혼합 모델의 전문가를 전부 센 것. 4B보다 큰 모델에서 비율이 15% 아래면 워밍업이 안 됐거나 기계가 바빴던 거야. 다시 돌려.

Progress

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

댓글 0

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

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