Repository navigation
fix: Metal backend performance on AMD discrete GPUs - #19527
Open
ai-janitor wants to merge 1 commit into
Open
ai-janitor wants to merge 1 commit into
ai-janitor wants to merge 1 commit into
Conversation
Fixes two critical issues with Metal backend on AMD/Intel discrete GPUs: 1. Auto-disable concurrent dispatch on non-Apple GPUs - MTLDispatchTypeConcurrent has broken memory barriers on AMD - Causes L2 cache coherency issues leading to corrupted output - Now auto-detects via supports_gpu_family_apple7 and uses serial dispatch 2. Use Managed buffer mode on discrete GPUs for cached PCIe reads - Shared mode uses uncached PCIe reads (~3 MB/s) on discrete GPUs - Managed mode enables cached reads (~3 GB/s) with explicit sync - Adds didModifyRange after CPU writes, synchronizeResource for reads - Improves generation speed from ~11 t/s to ~60 t/s on AMD Radeon Pro 5300M Performance on AMD Radeon Pro 5300M: - Before: 11.5 t/s generation (corrupted output with concurrent dispatch) - After: 60+ t/s generation (correct output, 88% of Vulkan performance) Environment variable overrides for testing: - GGML_METAL_MANAGED_BUFFERS_DISABLE/ENABLE - GGML_METAL_CONCURRENCY_DISABLE (still works) Also adds bench_metal.sh with benchmark and conversation modes. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
lvalen91
added a commit
to lvalen91/llama.cpp
that referenced
this pull request
Mar 30, 2026
Cherry-picked fixes from ai-janitor's Metal AMD work:
1. Disable concurrent dispatch on non-UMA GPUs
- MTLDispatchTypeConcurrent + barriers don't sync AMD L2 cache
- Causes garbage output scaling with -ngl
2. Use MTLResourceStorageModeManaged for discrete GPUs
- StorageModeShared uses uncached PCIe reads (3 MB/s)
- Managed mode uses cached reads (3 GB/s) — 1000x bandwidth
3. Add scalar Flash Attention for non-simdgroup_mm GPUs
- Enables FA on AMD/Intel without simd_matrix intrinsics
- Fix simd_max(half) bug with -INF on RDNA
4. Use mul_mv_ext for large n on non-simdgroup_mm GPUs
- Removes ne11<=8 upper bound for AMD, reducing threadgroup
count from ~1.1M to ~280 for pp512
Tested on AMD Radeon PRO W6900X (32GB, RDNA2):
- Qwen3.5-27B Q4_K_M: 21 tok/s generation, correct output
- Qwen3-14B Q8_0: 29 tok/s gen, 138 tok/s prompt
Credit: github.com/ai-janitor (PRs ggml-org#19527, ggml-org#19600, ggml-org#19601)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
lvalen91
added a commit
to lvalen91/llama.cpp
that referenced
this pull request
Aug 14, 2026
Cherry-picked fixes from ai-janitor's Metal AMD work:
1. Disable concurrent dispatch on non-UMA GPUs
- MTLDispatchTypeConcurrent + barriers don't sync AMD L2 cache
- Causes garbage output scaling with -ngl
2. Use MTLResourceStorageModeManaged for discrete GPUs
- StorageModeShared uses uncached PCIe reads (3 MB/s)
- Managed mode uses cached reads (3 GB/s) — 1000x bandwidth
3. Add scalar Flash Attention for non-simdgroup_mm GPUs
- Enables FA on AMD/Intel without simd_matrix intrinsics
- Fix simd_max(half) bug with -INF on RDNA
4. Use mul_mv_ext for large n on non-simdgroup_mm GPUs
- Removes ne11<=8 upper bound for AMD, reducing threadgroup
count from ~1.1M to ~280 for pp512
Tested on AMD Radeon PRO W6900X (32GB, RDNA2):
- Qwen3.5-27B Q4_K_M: 21 tok/s generation, correct output
- Qwen3-14B Q8_0: 29 tok/s gen, 138 tok/s prompt
Credit: github.com/ai-janitor (PRs ggml-org#19527, ggml-org#19600, ggml-org#19601)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Can confirm that MacBook Pro 15 Vega 20 is also fixed thanks to this! Results for Qwen3-4B-Instruct-2507-Q4_0.gguf: |
This branch has not been deployed
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.
Summary
Vibe coded a fix for Metal backend on AMD/Intel discrete GPUs. Performance went from 5.3 t/s → 60.4 t/s (11x) on AMD Radeon Pro 5300M. Output went from garbage → correct.
What Was Broken
Bug 1 (PRIMARY): Broken
MTLDispatchTypeConcurrentcache coherency on AMDmemoryBarrierWithScope:MTLBarrierScopeBuffersdoesn't flush AMD Radeon's L2 cache between concurrent compute dispatches-ngl: more GPU layers = more garbageGGML_METAL_CONCURRENCY_DISABLE=1alone fixed all garbage outputBug 2: Uncached PCIe reads on discrete GPUs
MTLResourceStorageModeShareduses uncached PCIe (3 MB/s)eHostCached→ MoltenVK translates toManagedmode (3 GB/s, cached)How I Vibe Coded This
Binary timestamp tracing
mach_absolute_time()at 4 instrumentation pointsGPU timestamp profiling
GPUStartTime/GPUEndTimeon command buffersIsolation testing
simd_sum,simd_max,simd_shuffle_downall work correctly in isolationCross-backend validation
-ngl 0, 1, 2, 5+, 99to identify corruption thresholdMoltenVK source analysis
MVKCommandBuffer.mm:1063: MoltenVK usesMTLDispatchTypeSerialfor all computeMVKDevice.mm:790-800: DisablesplacementHeapsexplicitly on AMDMVK_CONFIG_SHADER_DUMP_DIRto compare memory access patternsThe Fix
Auto-detect non-Apple GPUs and force serial dispatch + managed buffers:
ggml-metal-context.m:133!supports_gpu_family_apple7ggml-metal-device.h:229use_managed_bufferspropertyggml-metal-device.m:790-800!has_unified_memoryggml-metal-device.m:1429,1512,1543ggml-metal-device.m:1614,1649didModifyRangesync after CPU writesNo configuration needed — auto-detects discrete GPUs and applies fix.
Environment variable overrides for testing:
GGML_METAL_MANAGED_BUFFERS_DISABLE/ENABLEGGML_METAL_CONCURRENCY_DISABLE(now redundant — auto-disabled)Benchmark Results
Qwen2.5-1.5B-Instruct Q4_K_M,
-ngl 99(all 28 layers on GPU)AMD Radeon Pro 5300M (4GB, RDNA 1) on Intel Mac:
11x generation speedup, correct output.
Testing
-ngl 0, 1, 2, 5+, 99Why MoltenVK Works (Vulkan on Same Hardware)
MTLDispatchTypeConcurrenteHostCachedtranslated toMTLResourceStorageModeManagedisAMDRDNAGPU()detection, disabledplacementHeapsSee
ggml-vulkan.cpp:4769— Vulkan backend already has AMD + macOS workarounds.Related Issues
100% vibe coded. Works on my machine — AMD Radeon Pro 5300M.
🤖 Generated with Claude Code