Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions TECHNICAL_REPORTS/1254-mtp-b1-gate-gpu-generation-20260820.en.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Technical Report: PR #1254 - B=1 MTP gate on GPU generation

**Date**: 2026-08-20
**Status**: Partial
**Languages**: Rust, Shell, Markdown
**Risk Level**: Medium

## Executive Summary

PR #1254 re-measures the batch-capable singleton MTP pairing on M3 Ultra and moves the static gate `mtp_b1_default` off the `has_neural_accelerator` proxy onto Apple GPU generation 15, the `use_qmv_wide` split. The gate was declining a pairing that measures 1.95x to 2.65x on that host. The change is deliberately partial: it does not close issue #1217, whose M1 Ultra acceptance criteria need hardware the measuring host is not.

## 1. Problem Statement

The B=1 MTP burst for batch-capable targets ran only where `has_neural_accelerator` held, which is M5 and nothing else. That policy rested on the founding measurement in #165: about 1.2 to 1.4x on M5 Max against a 0.75 to 0.96x regression on M1 Ultra. Both numbers predate #1194, #1199, #1203, #1208 and #1215. M3 Ultra had never been run on the pairing, so a generation-15 part was classified with generation 13 by a binary proxy that does not track the mechanism.

A second, quieter problem made the first one durable. Neither `scripts/bench_speculative.sh` nor `scripts/bench_block_width.sh` had a case for the Gemma 4 31B + bf16 assistant pairing, so the pairing the gate governs had no path through the #1215 measurement protocol. The founding numbers could not be reproduced or refreshed by the harness that exists to keep such numbers honest.

## 2. Technical Decisions

### 2.1 Discriminate on GPU generation rather than the Neural Accelerator

`AppleSiliconGen::wide_quantized_projections` encodes MLX's `use_qmv_wide` predicate reduced to its chip-dependent half: from generation 15 an affine-quantized projection at `M >= 2` runs as one wide pass, and on generation 13 the verify block runs as `K` narrow passes whose cost grows with the block. This is the mechanism the published round-cost model already credited for the host ordering, so the gate now reads the mechanism instead of a correlate of it. `Unknown` reads false, preserving today's decline for non-Apple hosts where a K-wide verify does not amortize at all (#638) and for Apple generations newer than the enumerated ones.

### 2.2 Leave generation 13 declining, and say why that is a conclusion

The width sweep made the conservative choice defensible rather than merely cautious. Round cost fits `0.83 + 0.170 K` classic steps for this pairing on M3 Ultra against `1.14 + 0.090 K` for the 12B pairing on the same host: the bf16 drafter costs about 1.9x as much per extra block position, and the two lines cross at K = 4. A naive transfer of M1 Ultra's published block-4 round cost of 2.71 across pairings would have predicted that generation 13 now clears break-even; carrying the slope ratio instead puts a block-4 round near 3.6 classic steps there, which the emitted tokens would only just cover, consistent with the founding regression.

### 2.3 Make the safety argument a test rather than a claim

The new predicate is strictly more permissive than the one it replaced, so no host loses a path it previously had. That is what makes it legitimate to change the gate on evidence from one host without re-measuring the others, so a unit test asserts the implication over every enumerated generation rather than leaving it as prose.

## 3. Change Summary

| Area | Change |
|---|---|
| `src/lib/mlxcel-core/src/hardware.rs` | New `AppleSiliconGen::wide_quantized_projections` predicate plus two unit tests (generation split, and the weaker-than-NA implication). |
| `src/server/batch/speculative_burst.rs` | `mtp_b1_default` third parameter becomes `wide_quantized_projections`; `mtp_b1_burst_enabled` reads it from `silicon_gen`; docstring rewritten to the new measurements with the two evidence limits stated; two new unit tests. |
| `src/server/batch/mtp_policy.rs` | Field renamed and re-sourced, so the adaptive policy's ambiguous-window fallback follows the same predicate. |
| `src/server/batch/scheduler.rs` | Decline-path comment updated to the new policy. |
| `scripts/bench_speculative.sh`, `scripts/bench_block_width.sh` | New `gemma31b` case in both, so the protocol can reach the pairing the gate governs. |
| `docs/benchmarks.md`, `docs/environment-variables.md` | 31B section rewritten with the measured rows; the claim that the pairing's speedup comes from B>1 windows and that its single-stream acceptance is too low is removed as falsified. |
| `docs/benchmark_results/mtp-b1-gate-m3ultra-2026-08-20.md` | New dated record: environment, three-prompt table, width sweep, round-cost fits, dispatch-path verification, and an explicit list of what was not measured. |

## 4. Review Findings

Two corrections were made during the work rather than after it.

The first was an analytical error caught by the width sweep. An earlier draft of the docstring and the env-var reference asserted that the round-cost model predicted post-#1203 M1 Ultra would now clear break-even on this pairing. That inference transferred a round cost between two pairings with different drafter dtypes, which the sweep then showed have slopes differing by 1.9x. Both statements were corrected before commit, and the results record now warns explicitly against transferring a round cost between these pairings at any width other than the crossing point.

The second was a PR-body defect. The sentence stating that the PR does not close #1217 itself contained the substring `close #1217`, which GitHub's closing-keyword parser matched without regard to the negation, so the PR was initially registered as auto-closing the issue it was written to leave open. The body was reworded and `closingIssuesReferences` re-verified as empty.

## 5. Validation

`cargo fmt --all --check` clean. `cargo clippy --profile test-fast --features metal,accelerate --all-targets` clean. 361 `server::batch` unit tests pass, plus the new `hardware` and `mtp_b1_default` tests.

Beyond the pure test seam, the gate was exercised through `Scheduler::mtp_b1_should_run` on the real checkpoints on the measuring host, with `MLXCEL_MTP_ADAPTIVE=0` so the static gate decides. `main` at `9e2c6675` declined the burst; this branch ran it (block 4, 80 tokens over 21 rounds, acceptance 0.921); `MLXCEL_ENABLE_MTP_B1=0` still declined. That before-and-after is on one host with one pair of checkpoints, which is what the acceptance criterion asked for and no more.

Recorded as part of validation: the offline `mlxcel generate` path does not consult this gate at all. `mtp_b1_default` has exactly one caller and `MtpPolicy` is built only in the server worker, so the bench harness runs the burst unconditionally. This is why the harness is the right instrument for deciding the gate, and why the two env vars the issue prescribes are inert there.

## 6. Related Work

Issue #1217 remains open by design. Its M1 Ultra rows and its M5 Max re-measurement need hardware that was not available, and the M3 Ultra Qwen rows it also asks for were already measured on current main under this protocol in #1215. Predecessors: #165 (the founding gate), #333 (adaptive policy, which falls back to this static default on an ambiguous window), #1203 (drafter projections quantized at load), #1215 (the measurement protocol and its guards).
62 changes: 62 additions & 0 deletions TECHNICAL_REPORTS/1254-mtp-b1-gate-gpu-generation-20260820.ko.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# 기술 보고서: PR #1254 - GPU 세대 기반 B=1 MTP 게이트

**작성일**: 2026-08-20
**상태**: 부분 완료
**언어**: Rust, Shell, Markdown
**위험도**: Medium

## 요약

PR #1254는 배치 가능 타깃의 단일 요청 MTP 페어링을 M3 Ultra에서 다시 측정하고, 정적 게이트 `mtp_b1_default`의 판별 기준을 `has_neural_accelerator` 대리 지표에서 Apple GPU 15세대의 `use_qmv_wide` 분기로 옮긴다. 기존 게이트는 해당 호스트에서 1.95배에서 2.65배까지 빨라지는 페어링을 거부하고 있었다. 이 변경은 의도적으로 부분 구현이며, 이슈 #1217을 닫지 않는다. 그 이슈의 M1 Ultra 관련 완료 조건은 측정 호스트가 갖추지 못한 하드웨어를 요구하기 때문이다.

## 1. 문제 정의

배치 가능 타깃의 B=1 MTP 버스트는 `has_neural_accelerator`가 참인 곳, 즉 M5에서만 동작했다. 이 정책의 근거는 #165의 최초 측정값이다. M5 Max에서 약 1.2배에서 1.4배, M1 Ultra에서 0.75배에서 0.96배의 성능 저하. 두 수치 모두 #1194, #1199, #1203, #1208, #1215보다 앞선다. M3 Ultra는 이 페어링으로 측정된 적이 아예 없었고, 그 결과 15세대 칩이 메커니즘을 반영하지 못하는 이진 대리 지표에 의해 13세대와 같은 부류로 묶였다.

문제를 오래 지속시킨 원인은 따로 있다. `scripts/bench_speculative.sh`와 `scripts/bench_block_width.sh` 어느 쪽에도 Gemma 4 31B + bf16 assistant 페어링 항목이 없었다. 게이트가 관장하는 바로 그 페어링이 #1215 측정 프로토콜을 통과할 경로를 갖지 못한 것이다. 수치의 신뢰성을 지키려고 만든 하니스가 정작 그 수치를 재현하거나 갱신할 수 없었다.

## 2. 기술적 선택과 그 이유

### 2.1 Neural Accelerator 대신 GPU 세대로 판별

`AppleSiliconGen::wide_quantized_projections`는 MLX의 `use_qmv_wide` 조건에서 칩에만 의존하는 부분을 추려 담는다. 15세대부터는 affine 양자화 프로젝션이 `M >= 2`에서 한 번의 넓은 패스로 처리되고, 13세대에서는 검증 블록이 `K`개의 좁은 패스로 나뉘어 블록이 커질수록 비용이 함께 늘어난다. 이미 문서화된 라운드 비용 모델이 호스트 간 순서를 설명할 때 지목한 메커니즘이 바로 이것이므로, 게이트는 이제 상관 지표가 아니라 메커니즘 자체를 읽는다. `Unknown`은 거짓으로 읽는다. K 폭 검증이 아예 상각되지 않는 비 Apple 호스트(#638)와 아직 열거하지 않은 최신 Apple 세대에서 기존의 거부 동작을 그대로 유지하기 위해서다.

### 2.2 13세대는 계속 거부하되, 그것을 결론으로 만들기

폭 스윕 덕분에 보수적인 선택이 단순한 신중함이 아니라 근거 있는 판단이 되었다. 이 페어링의 M3 Ultra 라운드 비용은 `0.83 + 0.170 K` classic step으로 맞춰지고, 같은 호스트의 12B 페어링은 `1.14 + 0.090 K`다. bf16 드래프터는 블록 위치 하나당 약 1.9배를 더 쓰며, 두 직선은 K = 4에서 교차한다. M1 Ultra의 공개된 블록 4 라운드 비용 2.71을 페어링만 바꿔 그대로 가져다 쓰면 13세대도 이제 손익분기를 넘는다는 결론이 나오지만, 기울기 비율을 반영하면 그곳의 블록 4 라운드 비용은 3.6 classic step 근처가 된다. 라운드가 내보내는 토큰 수로는 겨우 상쇄될 수준이고, 이는 최초 측정의 성능 저하와 부합한다.

### 2.3 안전성 논거를 주장이 아니라 테스트로

새 조건식은 교체 대상보다 엄격하게 더 관대하므로, 기존에 열려 있던 경로를 잃는 호스트는 없다. 한 호스트의 근거만으로 게이트를 바꾸어도 되는 이유가 바로 이 성질이다. 그래서 이 함의를 산문으로 남기지 않고, 열거된 모든 세대에 대해 단위 테스트로 검증한다.

## 3. 변경 요약

| 영역 | 변경 내용 |
|---|---|
| `src/lib/mlxcel-core/src/hardware.rs` | `AppleSiliconGen::wide_quantized_projections` 조건식 추가와 단위 테스트 2건(세대 분기, NA보다 약한 조건이라는 함의). |
| `src/server/batch/speculative_burst.rs` | `mtp_b1_default`의 세 번째 인자를 `wide_quantized_projections`로 변경, `mtp_b1_burst_enabled`가 `silicon_gen`에서 값을 읽도록 수정, 새 측정값과 근거의 두 가지 한계를 명시하도록 문서 주석 재작성, 단위 테스트 2건 추가. |
| `src/server/batch/mtp_policy.rs` | 필드 이름과 출처 변경. 적응형 정책이 프로파일링 결과가 모호할 때 쓰는 기본값도 같은 조건식을 따른다. |
| `src/server/batch/scheduler.rs` | 거부 경로 주석을 새 정책에 맞게 갱신. |
| `scripts/bench_speculative.sh`, `scripts/bench_block_width.sh` | 두 스크립트에 `gemma31b` 항목 추가. 게이트가 관장하는 페어링에 프로토콜이 닿을 수 있게 되었다. |
| `docs/benchmarks.md`, `docs/environment-variables.md` | 31B 절을 측정값으로 재작성. 이 페어링의 속도 향상이 B>1 윈도에서 나오며 단일 스트림 수용률이 너무 낮다는 서술은 사실과 달라 삭제. |
| `docs/benchmark_results/mtp-b1-gate-m3ultra-2026-08-20.md` | 날짜가 붙은 새 기록. 환경, 세 프롬프트 표, 폭 스윕, 라운드 비용 적합, 실제 디스패치 경로 검증, 측정하지 못한 항목의 명시. |

## 4. 리뷰 발견 사항

작업 도중 두 가지를 바로잡았다.

첫째는 폭 스윕이 잡아낸 분석 오류다. 문서 주석과 환경 변수 문서의 초고는 라운드 비용 모델이 #1203 이후의 M1 Ultra가 이제 손익분기를 넘을 것으로 예측한다고 단언했다. 이 추론은 드래프터 dtype이 다른 두 페어링 사이에서 라운드 비용을 그대로 옮긴 것인데, 스윕은 두 페어링의 기울기가 1.9배 차이 난다는 사실을 보여주었다. 두 서술 모두 커밋 전에 수정했고, 측정 기록에는 교차점이 아닌 폭에서 두 페어링의 라운드 비용을 서로 옮겨 쓰지 말라는 경고를 명시했다.

둘째는 PR 본문의 결함이다. 이 PR이 #1217을 닫지 않는다고 적은 문장 자체에 `close #1217`이라는 문자열이 들어 있었고, GitHub의 종료 키워드 파서는 부정 표현을 고려하지 않고 이를 인식했다. 열어 두려고 쓴 문장 때문에 PR이 이슈를 자동으로 닫도록 등록된 것이다. 본문을 고쳐 쓰고 `closingIssuesReferences`가 비어 있음을 다시 확인했다.

## 5. 검증

`cargo fmt --all --check` 통과. `cargo clippy --profile test-fast --features metal,accelerate --all-targets` 경고 없음. `server::batch` 단위 테스트 361건과 새로 추가한 `hardware`, `mtp_b1_default` 테스트가 모두 통과한다.

순수 함수 테스트 경계를 넘어, 측정 호스트에서 실제 체크포인트로 `Scheduler::mtp_b1_should_run` 경로를 직접 통과시켰다. 정적 게이트가 판단하도록 `MLXCEL_MTP_ADAPTIVE=0`을 설정한 상태에서, `9e2c6675` 시점의 `main`은 버스트를 거부했고, 이 브랜치는 버스트를 실행했으며(블록 4, 21라운드에 80토큰, 수용률 0.921), `MLXCEL_ENABLE_MTP_B1=0`은 여전히 거부했다. 한 호스트와 한 쌍의 체크포인트에서 확인한 전후 비교이며, 완료 조건이 요구한 범위가 정확히 거기까지다.

검증 과정에서 함께 기록한 사실이 하나 있다. 오프라인 `mlxcel generate` 경로는 이 게이트를 전혀 참조하지 않는다. `mtp_b1_default`의 호출자는 하나뿐이고 `MtpPolicy`는 서버 워커에서만 생성되므로, 벤치 하니스는 모든 호스트에서 버스트를 무조건 실행한다. 게이트를 판단하는 계측 도구로 이 하니스가 적절한 이유이자, 이슈가 지정한 두 환경 변수가 그곳에서는 아무 효과가 없는 이유이기도 하다.

## 6. 관련 작업

이슈 #1217은 의도적으로 열린 상태로 둔다. M1 Ultra 측정과 M5 Max 재측정은 확보하지 못한 하드웨어가 필요하고, 함께 요구된 M3 Ultra Qwen 측정값은 이미 #1215에서 같은 프로토콜로 현재 main에 기록되어 있다. 선행 작업: #165(최초 게이트), #333(적응형 정책, 프로파일링이 모호할 때 이 정적 기본값으로 되돌아온다), #1203(로드 시점 드래프터 프로젝션 양자화), #1215(측정 프로토콜과 그 보호 장치).
Loading