Skip to content

Fix custom metal kernel cache collision for same name, different source (#3832)#3833

Open
katlun-lgtm wants to merge 1 commit into
ml-explore:mainfrom
katlun-lgtm:fix/metal-kernel-cache-3832
Open

Fix custom metal kernel cache collision for same name, different source (#3832)#3833
katlun-lgtm wants to merge 1 commit into
ml-explore:mainfrom
katlun-lgtm:fix/metal-kernel-cache-3832

Conversation

@katlun-lgtm

Copy link
Copy Markdown
Contributor

Summary

Fixes #3832. Two fast.metal_kernel instances that share a name but have different source, dispatched before a single mx.eval, silently execute the first kernel's compiled code for the second — wrong results, no error.

Cause

The custom-kernel library cache is keyed by name_ alone (backend/metal/custom_kernel.cpp), with a hand-rolled "if the source changed, clear_library(name_) and rebuild" invalidation. That is safe across eval boundaries (the command buffer has completed before the clear) but not within one batch: the second kernel clears and rebuilds the shared name_ slot while the first kernel's dispatch is still pending in the same command buffer, and the second ends up bound to the first's code.

Fix

Key the library by name_ + source_ + compile_options_, so kernels that differ in any of those map to distinct libraries — and therefore distinct pipelines (the pipeline cache is already keyed by (library, function)). They now coexist for the whole batch instead of thrashing one slot, and the fragile clear-and-rebuild (with its CustomKernelCache bookkeeping) is removed. compile_options_ is still passed to get_library, so a kernel's math mode is honored.

The library map key is never used as a Metal identifier (only as a cache key), so the full source string is a collision-free key. A content hash — as suggested in the issue — would have a (tiny) collision chance that could alias two distinct sources and silently reintroduce exactly this bug, so the full string is used instead.

Test

Adds test_custom_kernel_same_name_different_source_one_eval: two same-name / different-source kernels evaluated in a single batch must each return their own result. It fails before this change and passes after.

Validation

Built against main; python/tests/test_fast.py passes (27/27, including the new test and the compile-options / caching tests), and the issue's repro now returns the correct [100, 101, 102] instead of [0, 2, 4].

Thanks to @Jinge-Wang for the precise report and minimal repro.

…ce (ml-explore#3832)

Two fast.metal_kernel instances sharing a name but with different sources,
dispatched before a single mx.eval, silently ran the first kernel's compiled
code for the second (wrong results, no error). The library cache was keyed by
name alone with a hand-rolled clear-and-rebuild of that one slot, which is not
safe within a single command batch.

Key the library by name + source + compile options so kernels that differ in
any of those get distinct libraries (and distinct pipelines) that coexist for
the whole batch, instead of thrashing one slot. compile_options_ is still
passed to get_library so per-kernel math mode is honored. The map key is never
used as a Metal identifier, so the full source is a collision-free key.

Adds a regression test (two same-name/different-source kernels in one eval).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] fast.metal_kernel: same-name kernels with different sources silently run stale code within one eval batch

1 participant