Skip to content

ggml : speed up batch-1 CPU decode, align large allocations - #27478

Open
matevz-kovacic wants to merge 2 commits into
ggml-org:masterfrom
matevz-kovacic:batch1-decode-attn-align
Open

ggml : speed up batch-1 CPU decode, align large allocations#27478
matevz-kovacic wants to merge 2 commits into
ggml-org:masterfrom
matevz-kovacic:batch1-decode-attn-align

Conversation

@matevz-kovacic

@matevz-kovacic matevz-kovacic commented Aug 21, 2026

Copy link
Copy Markdown

Overview

Bbatch-1 decode attention process KV one position at a time. It uses scalar expf per position, rescales accumulator when new max is fund and converts Q to F16 then widens back (x86). The loop-carried max/rescale is serialized back.
Tiled path can't be uilized. because it requires GGML_FA_TILE_Q query rows, but decode only has one.

This PR: vlocks the KV loop, rescaling once every 32 elements. Keeps Q in F32 where K is F16, and mixed F16xF32 dor exists.
Computes four K.Q dot products per Q load and accumulates V in F32 where a mixed multiply-add is used.
For Linux only: aligns allocations >=r 4MiB to 2MiB + madvise(MADV_HUGEPAGE), retrying at ordinary alignment on failure.
Also the fallback is implemented when tiling is not feasible (quantized K/V, mismatched K/V types, DV not a multiple of the vector width, 2..63 query rows); this is not evaluated in the measurements.

Additional information

Qwen3-30B-A3B Q4_K_M, tg at 8192-token context, THP madvise:

  • Ryzen 7 9700X +15.29% (attention change alone +10.67%)
  • Neoverse-N1 +9.22% (attention change alone +7.43%)
  • i5-13400 Win +38.78% (attention change only there; noisy desktop, directional)
  • under THP always: +12.00% / +6.61%, and +7.52% / +5.57% attention-only
  • measured on base 6e62ba5, gcc-14, -march=native / -mcpu=neoverse-n1

Correctness

  • test-backend-ops test -o FLASH_ATTN_EXT -b CPU: 5141/5141 on 8 build variants, x86-64 and AArch64

Scope limits:

  • one head geometry measured end-to-end: DV=128
  • DV=256 loses ~3.5% on x86 built with GGML_NO_INTERDOT_X4 (F32-Q dot on, four-output helper off). AArch64 compiles neither, so it is not affected
  • attention change alone regresses pp512 by 2.194%; bundled with the allocator it is +1.19%. Windows and macOS build the attention half alone. A dead-code control reproduces the regression with the branch never taken, so it is code layout, not algorithmic
  • at one query row master with -fa 0 is already 13.435 t/s against 11.948 with FA on; patched FA reaches 13.599. Someone already running -fa 0 gains ~0.3%
  • the per-thread scratch grows by one KQ block; the decode work-size term in the graph planner is updated to match

Related work

Commit 9b62573 advises the 2 MiB-aligned interior without changing alignment; it came in PR #22378, a draft closed unmerged

PR #22022 is open and complementary: it hints the mmap path for weights, which this change does not cover

Requirements

I used an AI coding assistant throughout this work, driven by Active Model, a private research harness of my own design (public result archive), with human review at every step. Specifically:

  • Code. The attention restructuring and the allocator change were AI-written to my direction. I reviewed every line, I am responsible for all of it, and I can explain any part of it on request.
  • Measurement and verification. The pre-registered factorial and alignment experiments, and the re-derivation of every figure in this description from the raw per-invocation artifacts were carried out with AI assistance. Claims in this description were cross-checked against those artifacts rather than against summary files.
  • This description. I wrote this description. AI checked it for factual errors against the measurement artifacts.

I directed the work, made the design and reporting decisions, reviewed each change before it was kept, and take full responsibility for everything submitted here. No part of this was submitted by an autonomous agent acting without my oversight.

@ggml-gh-bot

ggml-gh-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown

Hi @matevz-kovacic, thanks for your contribution!

Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:

  • PR Template not respected: Please respect the template when creating a new pull request. Make sure to fill out all required sections.

  • AI-generated content: While code is allowed to be generated by AI, please write the PR description and commit messages on your own without the help of AI.


Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below.

@ggml-gh-bot ggml-gh-bot Bot added the draft PR will be changed to draft by github-actions bot label Aug 21, 2026
@github-actions github-actions Bot added the ggml changes relating to the ggml tensor library for machine learning label Aug 21, 2026
@github-actions
github-actions Bot marked this pull request as draft August 21, 2026 09:24
@github-actions github-actions Bot removed the draft PR will be changed to draft by github-actions bot label Aug 21, 2026
Two changes to the same workload.

ggml_compute_forward_flash_attn_ext_f16_one_chunk block the loop over KV.
Therefore the softmax accumulator is rescaled at most once per block instead of
on every new maximum. Consume Q directly in F32 where a vectorized mixed F16xF32
dot exists and K is F16. Compute four K.Q dots from one Q load, and accumulate
an F16 V in F32 where a vectorized mixed multiply add exists.

For Linux only: In ggml_aligned_malloc we align allocations of at least 4 MiB to
2 MiB and hint them with madvise(MADV_HUGEPAGE). If that fails we retry so
it is never worse than before.

Result for Qwen3-30B-A3B Q4_K_M: single sequence token generation at an
8192 token context and transparent huge pages in madvise mode: +15.29% on a
Ryzen 7 9700X and +9.22% on a Neoverse-N1. If we measure the attention change
alone the result is +10.67% and +7.43%. Under THP always those four figures
are +12.00%, +6.61%, +7.52% and +5.57%.

The two changes were measured separately as a 2x2 with THP mode as a third
factor.

Assisted-by: Claude Opus 5
@matevz-kovacic
matevz-kovacic force-pushed the batch1-decode-attn-align branch from 4cf3846 to 69be7ac Compare August 21, 2026 10:51
@matevz-kovacic
matevz-kovacic marked this pull request as ready for review August 21, 2026 10:56
Comment thread ggml/src/ggml-cpu/vec.h Outdated
int i = 0;
float f0 = 0.0f, f1 = 0.0f, f2 = 0.0f, f3 = 0.0f;
#if defined(__AVX512F__)
__m512 a0 = _mm512_setzero_ps(), a1 = _mm512_setzero_ps();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

use macroses that simplify repetable code)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good point.

Loop body now uses the existing GGML_F32_VEC / GGML_F16_VEC macros instead of hand-written intrinsics. The AVX2 reduce that was written out four times is now one local macro, GGML_FA_HSUM.

The existing reduce isn't used because GGML_F32_VEC_REDUCE folds an array of accumulators into a single scalar, but this function needs four independent outputs.

The generated code is unchanged: gcc emits byte-identical .text on AVX-512, AVX2 and SSE2; clang differs only by one instruction's scheduling on AVX2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ggml changes relating to the ggml tensor library for machine learning

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants