metal : split graphs into 4 command buffers on iOS - #275
Open
jasontitus wants to merge 2 commits into
Open
jasontitus wants to merge 2 commits into
jasontitus wants to merge 2 commits into
Conversation
iOS discards a command buffer that has run for about 5 s of GPU time when another GPU client (such as the display compositor) is waiting, and the graph fails with "Discarded (victim of GPU error/recovery)" (kIOGPUCommandBufferCallbackErrorInnocentVictim). With the default of one extra command buffer, the one holding ~90% of a 512-token prefill graph of a 27B model runs 5-7 s on an iPhone 17 Pro Max and failed in 10 of 50 runs; each recorded failure was that command buffer, discarded after 5.0 s. With 4 (1.6-2.3 s each) none of 10 runs failed and pp512 speed was unchanged. Output is bitwise identical for 1, 4 and 8 (M5 Max, full model). - default n_cb = 4 on iPhone-class OSes (iOS, iPadOS, visionOS, tvOS; not Mac Catalyst), 1 elsewhere; GGML_METAL_N_CB=1..8 overrides - with an abort callback set, use 1: only command buffers 0 and 1 are committed on that path, so the main thread's buffer (index n_cb) would never run with n_cb > 1 and synchronize would hang - ggml_metal_free releases all GGML_METAL_MAX_COMMAND_BUFFERS + 1 command buffers - warn above 4 instead of above 2 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHqq1nTe46u7euASncLsee
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHqq1nTe46u7euASncLsee
jasontitus
added a commit
to jasontitus/llama.cpp
that referenced
this pull request
Sep 26, 2026
…(PQ2_0 two columns) measured and ready Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHqq1nTe46u7euASncLsee
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.
Overview
On iOS, a Metal command buffer that runs for about 5 s of GPU time while another GPU client (such as the display compositor) is waiting gets discarded, and the graph fails with
Discarded (victim of GPU error/recovery) (00000005:kIOGPUCommandBufferCallbackErrorInnocentVictim)(llama_decodereturns -3). The Metal backend encodes a graph as a small first command buffer plusn_cb= 1 more holding ~90% of the nodes, so a long prefill ubatch on a phone is one multi-second command buffer.This PR makes the default
n_cb4 on iPhone-class OSes (iOS, iPadOS, visionOS, tvOS; not Mac Catalyst) and keeps 1 elsewhere;GGML_METAL_N_CB=1..8overrides it. Output is unchanged: splitting only moves command-buffer boundaries.It also fixes two latent issues that a default above 1 exposes:
graph_computecommits only command buffers 0 and 1 (later ones only when capturing). The main thread's buffer is indexn_cb, son_cb > 1hung insynchronize.n_cbis now clamped to 1 while an abort callback is set.ggml_metal_freereleasedGGML_METAL_MAX_COMMAND_BUFFERSof the+ 1command buffers.Additional information
iPhone 17 Pro Max (A19 Pro), Ternary Bonsai 2 27B PTQ1_0, 512-token prefill, every run starting at nominal temperature:
pp512 with 4: 71.7 vs 71.4 tok/s. The decode cost of 4 on the phone is not measured yet; macOS is unchanged (the default stays 1 there).
M5 Max (macOS): full-model logits bitwise identical with
GGML_METAL_N_CB1, 4 and 8 (700 tokens); test-backend-ops MUL_MAT and FLASH_ATTN_EXT pass with 1 and 4; an abort-callback reproducer completes with 1, 4 and 8 (it hung with 4 before the clamp). iOS device build checked.Requirements