Skip to content

metal: depthwise convolution via a ported MLX kernel (2-14x) - #2550

Open
czoli1976 wants to merge 2 commits into
sonos:mainfrom
czoli1976:feat/metal-depthwise-conv
Open

metal: depthwise convolution via a ported MLX kernel (2-14x)#2550
czoli1976 wants to merge 2 commits into
sonos:mainfrom
czoli1976:feat/metal-depthwise-conv

Conversation

@czoli1976

Copy link
Copy Markdown
Contributor

Stacked on #2549 — its commit is the first of the two here, so this is only
reviewable once that lands.

#2549 routes regular convolutions to mlx's implicit-GEMM kernel but leaves
depthwise ones on the direct kernel, which is most of a mobile vision model:
MobileNet v2 is 17 depthwise convolutions out of 18. This ports mlx's depthwise
kernel and routes the shapes it covers — one channel per group in and out,
kernel up to 7×7, stride up to 2, channels a multiple of 16, dilation 1 — using
mlx's own gate.

No weight reorder is needed. Once the shared rule has put the kernel in OIHW,
a depthwise kernel is [C, 1, kH, kW], which is the same bytes as the
[C, kH, kW, 1] this kernel indexes, so depthwise is deliberately excluded from
the OHWI move that #2549 does for regular convolutions.

Numbers

bench_depthwise (added here), M1 Pro, ms per dispatch, best of 10, SameUpper.
MobileNet-shaped layers:

shape direct mlx
56×56×192, 3×3 1.441 0.099 14.5×
112×112×32, 3×3 1.221 0.100 12.2×
112×112×144, 3×3 s2 0.783 0.117 6.7×
28×28×384, 3×3 0.341 0.075 4.5×
14×14×576, 3×3 0.149 0.048 3.1×
7×7×960, 3×3 0.064 0.030 2.2×

f16 tracks within a few percent (1.3×–9.5× over the same set).

End to end, MobileNet v2 (TF, NHWC, 224×224) on --metal:

ms/inference
main 14.8
this PR + #2549 11.1

1.34×, three interleaved runs each (14.8/14.8/15.1 against 11.1/11.8/12.1),
same predicted class as the CPU path. Worth being clear that the end-to-end gain
is much smaller than the kernel gain: MobileNet spends most of its time in the
pointwise 1×1 convolutions, which already lower to MetalMlxGemm, not in the
depthwise layers this touches.

Validation

cargo test -p tract-metal --release: 93 passed, one failure —
test_mfa_attention_causal_const_is_noop, pre-existing on main and unrelated
(#2546). New tests cover 3×3 same, 3×3 stride 2, 5×5 valid, f16, a non-square
input, and an end-to-end case through MetalTransform. fmt and clippy clean.

Measured on an M1 Pro only — I have an M4 available but it was asleep when this
was finished, so unlike #2549 this one carries no second-device numbers yet. The
gate is mlx's own and the kernel is verbatim, but if you would rather see M4
numbers before it lands, say so and I will add them.

mlx (ml-explore/mlx) is MIT, Copyright (c) 2023-2025 Apple Inc.; attributed in
the source header, flattened from a pinned commit. The same flatten also brings
mlx's Winograd kernels into the file unused — I measured that path separately and
it does not beat the implicit-GEMM one, so nothing dispatches to it.

🍍

czoli1976 and others added 2 commits August 1, 2026 22:00
The Metal convolution was a direct kernel computing one output position per
thread, which leaves most of the GPU idle: on this M1 Pro it runs a
56x56x64 -> 128 3x3 layer at about 20 GFLOP/s. Port mlx's tiled
implicit-GEMM conv as owned .metal source and route NHWC f16/f32
single-group 2D convolutions to it, leaving every other shape on the
direct kernel. The shared rewrite puts kernels in OIHW, which the direct
kernel indexes, so a metal-local rule reorders eligible ones into the
OHWI layout the ported kernel wants - from whichever layout the exporter
used, and into a constant, since the metal transform does not declutter
afterwards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Depthwise convolutions were left on the direct kernel by the
implicit-GEMM path, which is most of a mobile vision model: MobileNet v2
is 17 depthwise convolutions out of 18. Port mlx's depthwise kernel and
route the shapes it covers - one channel per group in and out, kernel up
to 7x7, stride up to 2, channels a multiple of 16. The OIHW kernel tract
already hands over is laid out the way this kernel indexes it, so no
reorder is needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant