Repository navigation
[PyTorch-MPS] Add output logprobs and MLX-LM parity - #1001
Merged
Merged
Conversation
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 5, 2026 00:16
a1b3e96 to
3d0bd58
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
2 times, most recently
from
October 5, 2026 03:57
506f4ef to
db92aba
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 5, 2026 04:37
db92aba to
813c02a
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 5, 2026 05:02
813c02a to
536381f
Compare
This was referenced Oct 5, 2026
Open
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 5, 2026 06:46
536381f to
58876db
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 5, 2026 06:49
58876db to
363849a
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 5, 2026 18:04
363849a to
ec4db35
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 5, 2026 18:25
ec4db35 to
2cacb77
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 5, 2026 18:36
2cacb77 to
33a040a
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
2 times, most recently
from
October 5, 2026 20:29
bc64072 to
fca6c6d
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 6, 2026 19:11
fca6c6d to
689b165
Compare
WindChimeRan
marked this pull request as ready for review
October 6, 2026 21:10
Collaborator
|
Thanks! Blocked on #858 (stacked). Please also drop the no-op |
ericcurtin
marked this pull request as draft
October 7, 2026 08:32
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 7, 2026 20:46
e3f1d61 to
5c25823
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 7, 2026 21:25
5c25823 to
3c96c4e
Compare
WindChimeRan
marked this pull request as ready for review
October 8, 2026 04:01
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
2 times, most recently
from
October 8, 2026 05:37
7481f46 to
cc924fe
Compare
WindChimeRan
marked this pull request as draft
October 13, 2026 06:40
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
2 times, most recently
from
October 13, 2026 22:03
78c38b3 to
6e95be3
Compare
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
2 times, most recently
from
October 13, 2026 22:53
39d97ba to
3f0c9af
Compare
Collaborator
Author
|
Post review changes
|
WindChimeRan
marked this pull request as ready for review
October 13, 2026 23:01
ricky-chaoju
approved these changes
Oct 14, 2026
ricky-chaoju
left a comment
Collaborator
There was a problem hiding this comment.
Tried the MPS parity with Qwen3-0.6B and got 33/7/0 on both batch sizes, logprobs look right too. LGTM
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 14, 2026 04:45
3f0c9af to
1e85406
Compare
Use upstream vLLM scoring for MPS output-token logprobs. Reuse the existing MLX-LM parity flow with CPU fallback disabled and leave dtype resolution to the model loaders. Signed-off-by: Ranran <ranranhaoranzhang@gmail.com>
WindChimeRan
force-pushed
the
ran/mps-backend-benchmark
branch
from
October 14, 2026 05:02
1e85406 to
c5a51cf
Compare
WindChimeRan
added a commit
that referenced
this pull request
Oct 15, 2026
Stacked on #1001 (PR5). Qwen3's eager MPS RoPE path performs separate cache lookup, rotation, and output assembly operations. This PR applies packed Q/K rotation in one Metal dispatch, using vLLM's existing model definitions and cosine/sine caches. The kernel handles FP16/BF16 full-head NeoX RoPE. Other rotary variants retain vLLM's native implementation. The shader is bundled in the wheel and loaded through `torch.mps.load_metallib`; no new dependency or C++ launcher is added. ```bash VLLM_METAL_BACKEND=mps PYTORCH_ENABLE_MPS_FALLBACK=0 \ vllm serve Qwen/Qwen3-0.6B --dtype bfloat16 ``` ## Parity PyTorch-MPS vs native MLX-LM on [b32095f](b32095f), using matching BF16 checkpoints and input token IDs: 40 prompts, 16 output tokens, request batches 1 and 2, prefix caching off, and optional CPU fallback disabled. Cells report **EXACT / TOP_K_MATCH / FAIL**. Top-5 requires mutual membership at the first divergence; later continuation tokens are not compared. All 320 MPS output token sequences are identical to PR4. | Model | Batch 1: exact / top-5 / fail | Batch 2: exact / top-5 / fail | |---|---:|---:| | Qwen3-0.6B | 29 / 11 / 0 | 29 / 11 / 0 | | Hunyuan-1.8B-Instruct | 38 / 2 / 0 | 37 / 3 / 0 | | OLMo-2-0425-1B-Instruct | 29 / 11 / 0 | 32 / 8 / 0 | | LFM2.5-1.2B-Instruct | 31 / 9 / 0 | 34 / 6 / 0 | ```bash VLLM_METAL_BACKEND=mps PYTORCH_ENABLE_MPS_FALLBACK=0 MLX_ENABLE_TF32=0 \ python tools/check_parity.py --model /path/to/checkpoint \ --top-k 5 --max-tokens 16 --batch-size 1 2 \ --gpu-memory-utilization 0.15 --output-dir rope-parity ``` <details> <summary>Checkpoints and environment</summary> Apple M5 Pro, 64 GiB; vLLM 0.31.0, PyTorch 2.13.0, MLX 0.32.1, MLX-LM 0.32.0. Runs use prebuilt native artifacts. The built wheel also completed Qwen3 generation with compiler imports blocked and TileLang absent; 448 fused RoPE calls were observed. | Checkpoint | Revision | |---|---| | Qwen/Qwen3-0.6B | `c1899de289a04d12100db370d81485cdf75e47ca` | | tencent/Hunyuan-1.8B-Instruct | `de940610ab5d45b4fedbd37e08444d0dc502837c` | | allenai/OLMo-2-0425-1B-Instruct | `48d788eca847d4d7548f375ad03d3c9312f6139e` | | LiquidAI/LFM2.5-1.2B-Instruct | `df58c174f05ff733f83f8cae10ea9298224c8006` | </details> ## Performance Qwen3-0.6B BF16, Sonnet 1024/128, 100 requests at 10/s, concurrency 32. Means of two fresh-server runs per path, in PR4/PR5/PR5/PR4 order. | Metric | PR4 MPS | PR5 MPS | Change | |---|---:|---:|---:| | Output tok/s ↑ | 461.49 | 503.93 | +9.2% | | Mean TTFT (ms) ↓ | 371.38 | 279.79 | -24.7% | | Mean TPOT (ms) ↓ | 57.77 | 52.70 | -8.8% | <details> <summary>Benchmark setup and individual runs</summary> PR4 [3f0c9af](3f0c9af) vs PR5 [b32095f](b32095f), on the same M5 Pro and software environment as the parity runs. Both paths use BF16, async scheduling, max model length/batched tokens 2048, block size 16, 2,340 KV blocks, prefix caching off, seed 0, greedy decoding, four warmups, and no logprobs. Each run completed 100 timed requests with 101,230 input tokens, 12,800 output tokens, identical input lengths, and zero preemptions. AC power; no recorded thermal/performance warnings. Desktop activity was not isolated. About 7.7 GiB of swap was already allocated; 7.5–33.8 MiB of swap-ins and no swap-outs were recorded per run. The individual results show the observed variation. | Run | Output tok/s | TTFT (ms) | TPOT (ms) | |---|---:|---:|---:| | 1-pr4 | 437.30 | 374.59 | 60.81 | | 2-pr5 | 501.00 | 276.59 | 53.02 | | 3-pr5 | 506.86 | 282.98 | 52.38 | | 4-pr4 | 485.68 | 368.17 | 54.74 | For each checkout, start a fresh server: ```bash VLLM_METAL_BACKEND=mps PYTORCH_ENABLE_MPS_FALLBACK=0 \ VLLM_USE_V2_MODEL_RUNNER=1 VLLM_USE_HW_AGNOSTIC=1 \ VLLM_ENABLE_V1_MULTIPROCESSING=1 VLLM_METAL_BUILD_FROM_SOURCE=0 MLX_ENABLE_TF32=0 \ vllm serve /path/to/Qwen3-0.6B --host 127.0.0.1 --port 8000 \ --dtype bfloat16 --model-impl auto --block-size 16 \ --max-model-len 2048 --max-num-seqs 32 --max-num-batched-tokens 2048 \ --gpu-memory-utilization 0.3 --kv-cache-memory-bytes 4294967296 \ --num-gpu-blocks-override 2340 --async-scheduling \ --no-enable-prefix-caching --generation-config vllm --seed 0 ``` After the server is ready, run the client from another terminal. Use a separate result directory for each run: ```bash vllm bench serve --backend vllm --base-url http://127.0.0.1:8000 \ --endpoint /v1/completions --model /path/to/Qwen3-0.6B \ --dataset-name sonnet --dataset-path sonnet.txt \ --sonnet-input-len 1024 --sonnet-output-len 128 \ --num-prompts 100 --request-rate 10 --max-concurrency 32 \ --temperature 0 --ignore-eos --seed 0 --num-warmups 4 \ --ready-check-timeout-sec 0 --save-result --save-detailed \ --result-dir /path/to/run-results --result-filename result.json ``` </details> Signed-off-by: Ranran <ranranhaoranzhang@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #880.
Add MPS output-token logprobs using upstream vLLM scoring so the existing
mlx-lmparity tool can check PyTorch-MPS serving. Both serving backends use the same reference and shared comparison/reporting code.Parity
Every serving run below uses PyTorch-MPS. Both tables use matching unquantized BF16 checkpoints and input token IDs: 40 prompts, 16 output tokens, request batch sizes 1 and 2, prefix caching off, and MPS CPU fallback disabled.
Cells report EXACT / TOP_K_MATCH / FAIL. Top-5 requires mutual membership at the first divergence; later continuation tokens are not compared.
PyTorch-MPS vs MLX-LM
Measured on 39d97baa using vLLM's default dtype resolution. All four MPS servers resolved to BF16.
PyTorch-MPS vs Transformers/MPS
I tried with Transformers/MPS and found it's unnecessary, so I delete it
Historical cross-check from 788f0786, preserved from before the Transformers adapter was removed. These are measured results; the current tool uses MLX-LM only.