Skip to content

hexagon: MUL_MAT and MUL_MAT_ID rework : 32x32 tiled weight repack, kernel-params, cached graphs - #24954

Merged
max-krasnyansky merged 78 commits into
ggml-org:masterfrom
qualcomm:hexagon-mm-new-weight-layout
Jun 24, 2026
Merged

hexagon: MUL_MAT and MUL_MAT_ID rework : 32x32 tiled weight repack, kernel-params, cached graphs#24954
max-krasnyansky merged 78 commits into
ggml-org:masterfrom
qualcomm:hexagon-mm-new-weight-layout

Conversation

@max-krasnyansky

@max-krasnyansky max-krasnyansky commented Jun 23, 2026

Copy link
Copy Markdown
Member

Overview

The main feature here is the new Qx32x32 tiled weight repack that replaces the original flat Qx4x2.
Weight processing is the main bottleneck in the original MatMul implementation and new tiled format addresses that.

The reason the PR is so chunky is because it's essentially not possible to change just one part of MatMul to use new tiled format and leave the rest as is. I had to re-write all of the quantized HVX vec_dot kernels in order to use this new tiled format. As part of the re-write I factored out all inner kernels into htp/hmx-mm-kernels and htp/hvx-mm-kernels and removed most (if not all) of the duplication between the HMX and HVX matmuls.

The new weight format is specifically designed for HMX so it makes sense to obsolete support for v68 and v69 and only keep v73 and up where we can always use HMX. It was actually a bit of struggle to match the performance of the original HVX implementation so forcing the legacy systems to use it just doesn't make sense.

The new implementation has a new MatMul solver that now runs on the host CPU and picks the right kernel and implements a clean HMX:tiled -> HVX:tiled -> HVX:flat fallback depending on the tensor dimensions and VTCM size requirements (flat in this case refers to the dyn.quantized activation layout).
To reduce the host CPU overhead (i.e. recomputing the matmul kernel params) I added a simple graph caching support that mimics what CUDA and Metal backends do already.

Also included are minor changes to follow up on #24592 (sprintf() -> snprinf(), etc)

Overall, these new MUL_MAT and MUL_MAT_ID kernels are faster on the prompt and much more robust for different tensor dimensions. Models like LFM2.5-8B-A1B-Q4_0.gguf and gemma-4-26B_q4_0-it.gguf now run well and produce correct output.

Additional information

This has been extensively tested on a bunch of devices: that cover all supported Hexagon versions.
Older devices do not benefit as much due to fewer supported HVX threads.

Some models might show a slight drop in token gen performance. I'm going to recover that in the Flash Attention followup that will reuse this new infra for computing kernel params on the host.

Details
## gemma-2-E2B_q4_0

Ventuno-Q
   prompt eval time = 1547.86 ms / 786 tokens (  1.97 ms per token,  507.80 tokens per second)  (vs 471.66 master)
          eval time = 4189.09 ms /  63 runs   ( 66.49 ms per token,   15.04 tokens per second)  (vs  14.48 master)

S24U
   prompt eval time =  908.55 ms / 741 tokens (  1.23 ms per token,  815.58 tokens per second)  (vs 719.98 master)
          eval time = 3266.78 ms /  63 runs   ( 51.85 ms per token,   19.29 tokens per second)  (vs  19.45 master)

S25+
   prompt eval time =  616.74 ms / 741 tokens (  0.83 ms per token, 1201.47 tokens per second)  (vs 1158.07 master)
          eval time = 2495.67 ms /  63 runs   ( 39.61 ms per token,   25.24 tokens per second)  (vs   25.41 master)

S26+
   prompt eval time =  450.06 ms / 741 tokens (  0.61 ms per token, 1646.45 tokens per second)  (vs 1433.78 master)
          eval time = 2139.42 ms /  63 runs   ( 33.96 ms per token,   29.45 tokens per second)  (vs   27.56 master)

X2-Elite
   prompt eval time =  438.87 ms / 741 tokens (  0.59 ms per token, 1688.41 tokens per second)
          eval time = 1844.81 ms /  63 runs   ( 29.28 ms per token,   34.15 tokens per second)

## Qwen3.5-2B-Q4_0.gguf

S24U 
  prompt eval time = 1275.41 ms / 742 tokens (  1.72 ms per token,  581.78 tokens per second)  (vs 556.46 master)
         eval time = 2917.78 ms /  63 runs   ( 46.31 ms per token,   21.59 tokens per second)  (vs  18.74 master)

S25+
   prompt eval time =  765.71 ms / 742 tokens (  1.03 ms per token,  969.03 tokens per second) (vs 962.09 master)
          eval time = 2308.81 ms /  63 runs   ( 36.65 ms per token,   27.29 tokens per second) (vs  26.63 master)

S26+
   prompt eval time =  578.70 ms / 742 tokens (  0.78 ms per token, 1282.19 tokens per second) (vs 1180.11 master)
          eval time = 2046.08 ms /  63 runs   ( 32.48 ms per token,   30.79 tokens per second) (vs   31.20 master)

X2-Elite
   prompt eval time =  589.82 ms / 742 tokens (  0.79 ms per token, 1258.02 tokens per second)
          eval time = 1694.80 ms /  63 runs   ( 26.90 ms per token,   37.17 tokens per second)

## LFM2.5-8B-A1B-Q4_0.gguf

S26+
   prompt eval time =  649.18 ms / 763 tokens (  0.85 ms per token, 1175.32 tokens per second)
          eval time = 1675.34 ms /  63 runs   ( 26.59 ms per token,   37.60 tokens per second)

X2-Elite
   prompt eval time =  674.82 ms / 763 tokens (  0.83 ms per token, 1185.67 tokens per second)
          eval time = 1521.95 ms /  63 runs   ( 24.16 ms per token,   41.39 tokens per second)

TODO: add X1-Elite numbers

Requirements

  • I have read and agree with the contributing guidelines
  • AI usage disclosure: Yes, lots of help from Antigravity on refactoring and reasoning about various optimizations based on the profile and traces.

@github-actions github-actions Bot added documentation Improvements or additions to documentation script Script related testing Everything test related python python script changes ggml changes relating to the ggml tensor library for machine learning Hexagon labels Jun 23, 2026
@max-krasnyansky
max-krasnyansky marked this pull request as ready for review June 23, 2026 20:07
@max-krasnyansky
max-krasnyansky requested review from a team and ggerganov as code owners June 23, 2026 20:07
@max-krasnyansky

Copy link
Copy Markdown
Member Author

@ggml-org/maintainers can I get the second approval please


// gpt-oss issue with Vulkan mmq_id
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_MXFP4, GGML_TYPE_F32, 32, 2, false, 2880, 32, 2880));
test_cases.emplace_back(new test_mul_mat_id(GGML_TYPE_Q4_0, GGML_TYPE_F32, 32, 2, false, 2880, 32, 2880));

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.

What's the reason for adding this?

@max-krasnyansky max-krasnyansky Jun 24, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah. I can remove if needed.
That row size 2880 (introduced by gpt-oss-20b) has been causing some pain in the kernels (not multiple of 256). Everytime I do a major update the first thing that breaks is that row-size :)
So I added q4_0 tests with that row size to cover most used q4_0 kernel.

Hopefully fixed for good now (because the new repack is happy with multiple of 32).

@max-krasnyansky
max-krasnyansky merged commit 8be759e into ggml-org:master Jun 24, 2026
30 of 32 checks passed
Geminihaha pushed a commit to Geminihaha/llama.cpp that referenced this pull request Jun 25, 2026
…ernel-params, cached graphs (ggml-org#24954)

* hex-mm: new weight layout and fusion updates

* hvx-mm: unroll the new tiled vec_dots to optimize hvx register util

* hex-mm: optimize dyn.quant format for q8_0 and q8_1 to reduce overhead in vec_dots.

* hvx-mm: parallel quantizer per block for large rows

* hvx-mm: simplify and futher optimize dyn.quant and vec_dots

* hvx-mm: keep intermediate per tile accumulators in fp16

* hmx-mm: optimize weight dequant by aligning the repacked tiles with the DMA

* hmx-mm: remove qweight scratch and just use vtcm_weight

* hmx-mm: remove all unused and obsolete code

* hmx-mm: the new tiled repack format is here to stay -- rename all x4x2 to _tiled

* hmx-mm: improve activation processing with dma prefetch

* hex-mm: fix hmx/hvx fallback logic and MUL_MAT_ID allocation (unbreaks OLMoE)

* hex-mm: align the weight tiles with dma just like we did in hmx-mm

* hex-mm: factor out common mm bits into htp/matmul-ops.h

* hex-mm: start moving mm kernel selection to the host

* hex-mm: move all of the matmul param compute into the host

* hmx-mm: restore pipelined mode

* hmx-mm: unroll the dequant functions to optimize register usage

* hmx-mm: further improve activation process

* hex-mm: use vtcm_seq_alloc for all vtcm allocations and define more common functions

* hex-mm: improve mm optimizer to acount for number of activation threads

* hex-mm: fix matmul-id kernel params selection (unbreaks OLMoE and LFM)

* hexagon: remove support for arch < v73 since HMX is now required for most use-cases

* hex-mm: cleanup naming for consistency

* hex-mm: make sure matmul fusion accounts for vtcm allocation

* hex-mm: minor cleanup for kernel_params definition

* hex-mm: replace hardcoded limits with proper checks for vtcm requirements

* hex-mm: add support for non-tiled mm as a fallback option and factor out hvx kernels into separate header

* hex-mm: remove unused functions

* hex-mm: add shorthand for MM_SELECT in run-tool script

* hvx-mm: factor out hvx/hmx microkernels and unify matmul entry and dispatch

* hex-mm: further cleanup matmul fallback path

* hex-mm: refactor matmul entry point and dispatch a bit further

* hexagon: update cmake build to enable hmx for everything

* hex-ops: optimize kernel_param updates and include summary in the logs

* hex-mm: add support for GGML_HEXAGON_MM_SELECT

* hex-mm: add hex-common header

* hex-mm: pass correct number of tasks to workpool

* hex-mm: add proper checks for no-work in dyn.quant tasks

* hex-mm: convert all quantizers into a macro

* hex-mm: fix hvx-flat fallback to pass all MUL_MAT tests

* hex-mm: vectorize q8_1 quantizer

* hex-mm: improve fused ffn mm stride handling

* hex-mm: consistent use of n_threads and pipeline in kernel_params

* hexagon: minor formatting

* hex-mm: update MUL_MAT_ID kernel_param handling to make sure host/npu are in sync

* hvx-mm: go back to accumulating in fp32 in tiled hvx kernels, more accurate and same perf

* hvx-mm: unroll the loops and remove masking that is not needed for tiled accums

* hmx-mm: optimize activation processing (slit loops, some unrolling, etc)

* hmx-mm: minor optimization for output processing

* hex-mm: consistent use of uint32_t and size_t in mm kernels

* hex-mm: remove legacy restrictions for rows to be multiple of 256

* hexagon: replace sprintf with snprintf

* hex-mm: relax hardcoded nrows checks and rely on VTCM size requirements

* hexagon: minor alignment fix

* hexagon: fix trailing spaces

* hex-mm: relax padding from 256 to 128 (leftovers)

* hex-mm: remove redundant checks for weight align to 128

we always use 2D dma for the weights and align them properly

* hmx-mm: MUL_MAT_ID better work distribution between hvx threads and hmx tracing

* hex-mm: specialize per-token mmid activation handling

* hex-profile: update python scripts to handle kernel-params section in the logging output

* hex-mm: move n_prefetch (aka dma_depth) into kernel params and remove unused fields

* hex-trace: use easier to parse format, simply and fix post-proc scripts

* hmx-mm: relax 32 row limit for output processing which helps utilization

* hmx-mm: use start-chunk idx for tracing info

* hmx-mm: parameterize activation dma pipeline

* hexagon: add support for simple graph caching to avoid recomputing kernel-params

* hex-mm: remove left-over repack functions

* hex-mm: tighten n_prefetch asserts

* hex-mm: remove duplicate round/align_up helper

* hexagon: cleanup common header used in host/npu

* hexagon: update early wakeup threshold

* hmx-mm: define cost constants and update solver to assume that repacked ne[1] is padded to 32

* hmx-mm: make precompute_matmul a bit more readable (split into smaller functions, etc)

* hex-mm: remove n_threads constraint

* hex-mm: minor formatting updates

* hex-mm: remove obsolete profiling logs

* hex-mm: restore hardcode gate to refuse lm-head to avoid repacking that tensor
adrianhoehne pushed a commit to adrianhoehne/llama.cpp that referenced this pull request Jul 5, 2026
…ernel-params, cached graphs (ggml-org#24954)

* hex-mm: new weight layout and fusion updates

* hvx-mm: unroll the new tiled vec_dots to optimize hvx register util

* hex-mm: optimize dyn.quant format for q8_0 and q8_1 to reduce overhead in vec_dots.

* hvx-mm: parallel quantizer per block for large rows

* hvx-mm: simplify and futher optimize dyn.quant and vec_dots

* hvx-mm: keep intermediate per tile accumulators in fp16

* hmx-mm: optimize weight dequant by aligning the repacked tiles with the DMA

* hmx-mm: remove qweight scratch and just use vtcm_weight

* hmx-mm: remove all unused and obsolete code

* hmx-mm: the new tiled repack format is here to stay -- rename all x4x2 to _tiled

* hmx-mm: improve activation processing with dma prefetch

* hex-mm: fix hmx/hvx fallback logic and MUL_MAT_ID allocation (unbreaks OLMoE)

* hex-mm: align the weight tiles with dma just like we did in hmx-mm

* hex-mm: factor out common mm bits into htp/matmul-ops.h

* hex-mm: start moving mm kernel selection to the host

* hex-mm: move all of the matmul param compute into the host

* hmx-mm: restore pipelined mode

* hmx-mm: unroll the dequant functions to optimize register usage

* hmx-mm: further improve activation process

* hex-mm: use vtcm_seq_alloc for all vtcm allocations and define more common functions

* hex-mm: improve mm optimizer to acount for number of activation threads

* hex-mm: fix matmul-id kernel params selection (unbreaks OLMoE and LFM)

* hexagon: remove support for arch < v73 since HMX is now required for most use-cases

* hex-mm: cleanup naming for consistency

* hex-mm: make sure matmul fusion accounts for vtcm allocation

* hex-mm: minor cleanup for kernel_params definition

* hex-mm: replace hardcoded limits with proper checks for vtcm requirements

* hex-mm: add support for non-tiled mm as a fallback option and factor out hvx kernels into separate header

* hex-mm: remove unused functions

* hex-mm: add shorthand for MM_SELECT in run-tool script

* hvx-mm: factor out hvx/hmx microkernels and unify matmul entry and dispatch

* hex-mm: further cleanup matmul fallback path

* hex-mm: refactor matmul entry point and dispatch a bit further

* hexagon: update cmake build to enable hmx for everything

* hex-ops: optimize kernel_param updates and include summary in the logs

* hex-mm: add support for GGML_HEXAGON_MM_SELECT

* hex-mm: add hex-common header

* hex-mm: pass correct number of tasks to workpool

* hex-mm: add proper checks for no-work in dyn.quant tasks

* hex-mm: convert all quantizers into a macro

* hex-mm: fix hvx-flat fallback to pass all MUL_MAT tests

* hex-mm: vectorize q8_1 quantizer

* hex-mm: improve fused ffn mm stride handling

* hex-mm: consistent use of n_threads and pipeline in kernel_params

* hexagon: minor formatting

* hex-mm: update MUL_MAT_ID kernel_param handling to make sure host/npu are in sync

* hvx-mm: go back to accumulating in fp32 in tiled hvx kernels, more accurate and same perf

* hvx-mm: unroll the loops and remove masking that is not needed for tiled accums

* hmx-mm: optimize activation processing (slit loops, some unrolling, etc)

* hmx-mm: minor optimization for output processing

* hex-mm: consistent use of uint32_t and size_t in mm kernels

* hex-mm: remove legacy restrictions for rows to be multiple of 256

* hexagon: replace sprintf with snprintf

* hex-mm: relax hardcoded nrows checks and rely on VTCM size requirements

* hexagon: minor alignment fix

* hexagon: fix trailing spaces

* hex-mm: relax padding from 256 to 128 (leftovers)

* hex-mm: remove redundant checks for weight align to 128

we always use 2D dma for the weights and align them properly

* hmx-mm: MUL_MAT_ID better work distribution between hvx threads and hmx tracing

* hex-mm: specialize per-token mmid activation handling

* hex-profile: update python scripts to handle kernel-params section in the logging output

* hex-mm: move n_prefetch (aka dma_depth) into kernel params and remove unused fields

* hex-trace: use easier to parse format, simply and fix post-proc scripts

* hmx-mm: relax 32 row limit for output processing which helps utilization

* hmx-mm: use start-chunk idx for tracing info

* hmx-mm: parameterize activation dma pipeline

* hexagon: add support for simple graph caching to avoid recomputing kernel-params

* hex-mm: remove left-over repack functions

* hex-mm: tighten n_prefetch asserts

* hex-mm: remove duplicate round/align_up helper

* hexagon: cleanup common header used in host/npu

* hexagon: update early wakeup threshold

* hmx-mm: define cost constants and update solver to assume that repacked ne[1] is padded to 32

* hmx-mm: make precompute_matmul a bit more readable (split into smaller functions, etc)

* hex-mm: remove n_threads constraint

* hex-mm: minor formatting updates

* hex-mm: remove obsolete profiling logs

* hex-mm: restore hardcode gate to refuse lm-head to avoid repacking that tensor
zommiommy pushed a commit to zommiommy/llama.cpp that referenced this pull request Aug 18, 2026
…ernel-params, cached graphs (ggml-org#24954)

* hex-mm: new weight layout and fusion updates

* hvx-mm: unroll the new tiled vec_dots to optimize hvx register util

* hex-mm: optimize dyn.quant format for q8_0 and q8_1 to reduce overhead in vec_dots.

* hvx-mm: parallel quantizer per block for large rows

* hvx-mm: simplify and futher optimize dyn.quant and vec_dots

* hvx-mm: keep intermediate per tile accumulators in fp16

* hmx-mm: optimize weight dequant by aligning the repacked tiles with the DMA

* hmx-mm: remove qweight scratch and just use vtcm_weight

* hmx-mm: remove all unused and obsolete code

* hmx-mm: the new tiled repack format is here to stay -- rename all x4x2 to _tiled

* hmx-mm: improve activation processing with dma prefetch

* hex-mm: fix hmx/hvx fallback logic and MUL_MAT_ID allocation (unbreaks OLMoE)

* hex-mm: align the weight tiles with dma just like we did in hmx-mm

* hex-mm: factor out common mm bits into htp/matmul-ops.h

* hex-mm: start moving mm kernel selection to the host

* hex-mm: move all of the matmul param compute into the host

* hmx-mm: restore pipelined mode

* hmx-mm: unroll the dequant functions to optimize register usage

* hmx-mm: further improve activation process

* hex-mm: use vtcm_seq_alloc for all vtcm allocations and define more common functions

* hex-mm: improve mm optimizer to acount for number of activation threads

* hex-mm: fix matmul-id kernel params selection (unbreaks OLMoE and LFM)

* hexagon: remove support for arch < v73 since HMX is now required for most use-cases

* hex-mm: cleanup naming for consistency

* hex-mm: make sure matmul fusion accounts for vtcm allocation

* hex-mm: minor cleanup for kernel_params definition

* hex-mm: replace hardcoded limits with proper checks for vtcm requirements

* hex-mm: add support for non-tiled mm as a fallback option and factor out hvx kernels into separate header

* hex-mm: remove unused functions

* hex-mm: add shorthand for MM_SELECT in run-tool script

* hvx-mm: factor out hvx/hmx microkernels and unify matmul entry and dispatch

* hex-mm: further cleanup matmul fallback path

* hex-mm: refactor matmul entry point and dispatch a bit further

* hexagon: update cmake build to enable hmx for everything

* hex-ops: optimize kernel_param updates and include summary in the logs

* hex-mm: add support for GGML_HEXAGON_MM_SELECT

* hex-mm: add hex-common header

* hex-mm: pass correct number of tasks to workpool

* hex-mm: add proper checks for no-work in dyn.quant tasks

* hex-mm: convert all quantizers into a macro

* hex-mm: fix hvx-flat fallback to pass all MUL_MAT tests

* hex-mm: vectorize q8_1 quantizer

* hex-mm: improve fused ffn mm stride handling

* hex-mm: consistent use of n_threads and pipeline in kernel_params

* hexagon: minor formatting

* hex-mm: update MUL_MAT_ID kernel_param handling to make sure host/npu are in sync

* hvx-mm: go back to accumulating in fp32 in tiled hvx kernels, more accurate and same perf

* hvx-mm: unroll the loops and remove masking that is not needed for tiled accums

* hmx-mm: optimize activation processing (slit loops, some unrolling, etc)

* hmx-mm: minor optimization for output processing

* hex-mm: consistent use of uint32_t and size_t in mm kernels

* hex-mm: remove legacy restrictions for rows to be multiple of 256

* hexagon: replace sprintf with snprintf

* hex-mm: relax hardcoded nrows checks and rely on VTCM size requirements

* hexagon: minor alignment fix

* hexagon: fix trailing spaces

* hex-mm: relax padding from 256 to 128 (leftovers)

* hex-mm: remove redundant checks for weight align to 128

we always use 2D dma for the weights and align them properly

* hmx-mm: MUL_MAT_ID better work distribution between hvx threads and hmx tracing

* hex-mm: specialize per-token mmid activation handling

* hex-profile: update python scripts to handle kernel-params section in the logging output

* hex-mm: move n_prefetch (aka dma_depth) into kernel params and remove unused fields

* hex-trace: use easier to parse format, simply and fix post-proc scripts

* hmx-mm: relax 32 row limit for output processing which helps utilization

* hmx-mm: use start-chunk idx for tracing info

* hmx-mm: parameterize activation dma pipeline

* hexagon: add support for simple graph caching to avoid recomputing kernel-params

* hex-mm: remove left-over repack functions

* hex-mm: tighten n_prefetch asserts

* hex-mm: remove duplicate round/align_up helper

* hexagon: cleanup common header used in host/npu

* hexagon: update early wakeup threshold

* hmx-mm: define cost constants and update solver to assume that repacked ne[1] is padded to 32

* hmx-mm: make precompute_matmul a bit more readable (split into smaller functions, etc)

* hex-mm: remove n_threads constraint

* hex-mm: minor formatting updates

* hex-mm: remove obsolete profiling logs

* hex-mm: restore hardcode gate to refuse lm-head to avoid repacking that tensor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation ggml changes relating to the ggml tensor library for machine learning Hexagon python python script changes script Script related testing Everything test related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants