Skip to content

[Metal] Tune splitk gemm dispatch conditions and partition sizes - #3087

Merged
awni merged 1 commit into
mainfrom
tune_splitk
Feb 2, 2026
Merged

awni merged 1 commit into
mainfrom
tune_splitk

Conversation

@awni

@awni awni commented Feb 1, 2026 •

Copy link
Copy Markdown
Member

This can dramatically speed up split-k gemms when done in sequence (26x speedup on M3 Ultra for one size 🤯 )

M4 Max

M N K Pre (ms) Post (ms) Speed Up
128 128 128 12.639 9.077 1.39
128 128 256 17.568 10.383 1.69
128 128 512 22.282 14.713 1.51
128 128 1024 34.351 15.765 2.18
128 128 2048 60.864 17.533 3.47
128 128 4096 114.788 19.789 5.80
128 128 8192 245.640 26.662 9.21
128 128 16384 476.670 40.392 11.80
512 512 128 10.691 14.260 0.75
512 512 256 16.785 18.631 0.90
512 512 512 26.678 25.447 1.05
512 512 1024 46.478 38.360 1.21
512 512 2048 85.256 64.395 1.32
512 512 4096 161.961 117.852 1.37
512 512 8192 317.836 226.321 1.40
512 512 16384 618.206 439.704 1.41

M3 Ultra

M N K Pre (ms) Post (ms) Speed Up
128 128 128 16.369 11.448 1.43
128 128 256 24.347 13.212 1.84
128 128 512 40.951 15.849 2.58
128 128 1024 72.140 16.552 4.36
128 128 2048 134.071 17.505 7.66
128 128 4096 259.238 20.488 12.65
128 128 8192 504.915 27.642 18.27
128 128 16384 994.301 38.196 26.03
512 512 128 15.589 15.998 0.97
512 512 256 22.657 18.180 1.25
512 512 512 36.709 22.717 1.62
512 512 1024 65.287 31.689 2.06
512 512 2048 119.640 50.662 2.36
512 512 4096 227.903 87.729 2.60
512 512 8192 443.597 159.841 2.78
512 512 16384 888.563 281.845 3.15

Here's the benchmark:

import time
import mlx.core as mx

mx.random.seed(0)


for M, N in [(128, 128), (512, 512), (512, 1024)]:
    for K in [128, 256, 512, 1024, 2048, 4096, 8192, 16384]:
        A = mx.random.normal(shape=(M, K)).astype(mx.bfloat16)
        B = mx.random.normal(shape=(K, N)).astype(mx.bfloat16)

        def fun(A):
            for i in range(60):
                C = A @ B
                # Hack to create a dependency chain so matmuls are in sequence
                A[0, 0] = C[0, 0]
            mx.eval(A)

        for _ in range(10):
            fun(A)

        tic = time.time()
        for i in range(10):
            fun(A)

        toc = time.time()
        s = toc - tic
        ms = 1e3 * (toc - tic)
        print(f"{M=}, {N=}, {K=}, {ms=:.3f}")

@angeloskath angeloskath left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

I wonder if it's worth doing sth for the 512x128 @ 512x128 (and maybe 1024x128 @ 512x128 which will also route to the split k now). But generally big impact small change ... awesome!

@awni

awni commented Feb 2, 2026

Copy link
Copy Markdown
Member Author

M4 Max

M N K Pre (ms) Post (ms) Speed up
128 128 128 11.619 8.703 1.34
128 128 256 16.766 10.067 1.67
128 128 512 21.567 13.085 1.65
128 128 1024 33.813 14.320 2.36
128 128 2048 60.392 16.586 3.64
128 128 4096 114.334 18.780 6.09
128 128 8192 245.209 25.695 9.54
128 128 16384 476.344 39.466 12.07
128 256 128 8.313 8.247 1.01
128 256 256 13.036 8.166 1.60
128 256 512 19.838 10.302 1.93
128 256 1024 33.160 15.163 2.19
128 256 2048 60.602 17.921 3.38
128 256 4096 116.467 23.496 4.96
128 256 8192 239.385 36.476 6.56
128 256 16384 477.037 65.675 7.26
256 256 128 8.458 8.272 1.02
256 256 256 13.134 9.437 1.39
256 256 512 19.840 12.115 1.64
256 256 1024 33.153 18.433 1.80
256 256 2048 61.959 28.771 2.15
256 256 4096 123.692 38.986 3.17
256 256 8192 242.372 65.100 3.72
256 256 16384 483.390 120.304 4.02
512 512 128 10.410 10.471 0.99
512 512 256 16.553 16.715 0.99
512 512 512 26.246 24.340 1.08
512 512 1024 46.567 37.386 1.25
512 512 2048 84.631 63.439 1.33
512 512 4096 161.382 117.075 1.38
512 512 8192 317.716 225.136 1.41
512 512 16384 616.977 436.999 1.41
512 1024 128 14.983 14.929 1.00
512 1024 256 22.967 22.830 1.01
512 1024 512 38.432 38.706 0.99
512 1024 1024 70.239 63.551 1.11
512 1024 2048 132.250 112.127 1.18
512 1024 4096 257.586 213.894 1.20
512 1024 8192 513.269 421.160 1.22
512 1024 16384 1034.962 851.288 1.22

@awni

awni commented Feb 2, 2026

Copy link
Copy Markdown
Member Author

M3 Ultra

M N K Pre (ms) Post (ms) Speed up
128 128 128 16.560 11.602 1.43
128 128 256 24.368 12.963 1.88
128 128 512 40.849 15.693 2.60
128 128 1024 72.331 16.468 4.39
128 128 2048 134.464 17.776 7.56
128 128 4096 258.870 20.623 12.55
128 128 8192 505.099 27.678 18.25
128 128 16384 991.701 38.251 25.93
128 256 128 15.836 15.596 1.02
128 256 256 22.989 13.201 1.74
128 256 512 37.048 16.122 2.30
128 256 1024 64.657 21.863 2.96
128 256 2048 119.839 22.639 5.29
128 256 4096 232.798 25.028 9.30
128 256 8192 456.507 33.446 13.65
128 256 16384 924.569 55.986 16.51
256 256 128 15.167 15.179 1.00
256 256 256 22.271 13.758 1.62
256 256 512 36.880 16.623 2.22
256 256 1024 64.507 22.757 2.83
256 256 2048 119.306 35.225 3.39
256 256 4096 230.668 38.267 6.03
256 256 8192 455.055 53.923 8.44
256 256 16384 907.307 89.993 10.08
512 512 128 15.295 15.425 0.99
512 512 256 22.426 22.747 0.99
512 512 512 36.432 22.496 1.62
512 512 1024 64.552 31.637 2.04
512 512 2048 119.548 50.961 2.35
512 512 4096 227.557 87.936 2.59
512 512 8192 443.485 160.607 2.76
512 512 16384 892.920 280.102 3.19
512 1024 128 16.006 15.965 1.00
512 1024 256 23.502 23.385 1.01
512 1024 512 37.413 37.448 1.00
512 1024 1024 65.283 47.908 1.36
512 1024 2048 119.681 79.249 1.51
512 1024 4096 228.620 141.513 1.62
512 1024 8192 446.631 264.204 1.69
512 1024 16384 897.480 527.691 1.70

@awni
awni merged commit b56782b into main Feb 2, 2026
16 checks passed
@awni
awni deleted the tune_splitk branch February 2, 2026 16:45
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.

2 participants