mudler / mudler/vllm.cpp

GLM-5.3-Flash stages paged KV one token at a time: ~180,000 copy-plus-synchronize round trips for an 8192-token prefill

Open
#2,716 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
423
Forks
53
Avg merge
20h 26m
Merged PRs (30d)
310

Description

Row: MODEL-MM-GLM53-FLASH

What

GLM-5.3-Flash stages its paged KV one token at a time, and each staged row
costs a device copy plus a full queue synchronize.

src/vllm/model_executor/models/glm5_next_kv.cpp:656-682, per DSA layer, two
loops each doing one Copy per token:

for (int64_t t = 0; t < b.cached_len; ++t) {
  const int64_t off = PagedRowOffset(lblocks, b.block_size, latent_row, t);
  uint8_t* row = Staging(&span, static_cast<size_t>(latent_row) * lat_elt);
  io.Read(lat.data, static_cast<size_t>(off) * lat_elt,
          static_cast<size_t>(latent_row) * lat_elt, row);

with the indexer twin at :674-681; StoreCaches mirrors it at :766/:795.

The synchronize is by design, and correct as written (:139-150): the
staging buffer is a single reused row (Staging, :167-170, "One per
LoadCaches / StoreCaches call"), so a deferred wait would read one row while
the driver is still writing the previous one.

Scale

Measured shape, inferred arithmetic. 11 deepseek_sparse_attention layers ×
8192 tokens × 2 caches = 180,224 round trips for one prefill. At
block_size = 32 a per-block coalesced staging would be 5,632 — a ~32×
reduction. The 8192 and the 11 are read from the model; the block size is not
pinned in this file, so treat the denominator as unconfirmed.

:668 (if (!lb.has_own_indexer) continue;) exempts shared layers from the
indexer half, which can only lower the real count.

Why it is not a one-line fix

The round trip cannot be batched without restructuring the staging buffer: it is
one row wide precisely so the synchronize is cheap and correct. Coalescing means
a per-block buffer and a different correctness argument, not just moving the
Synchronize out of the loop — doing only the latter reintroduces exactly the
hazard the comment at :139-150 describes.

Class

Performance. No wrong answer; the current shape is correct. Recorded in spec
prose as "OWED, and small", which undersells it — 180k synchronizes on a prefill
is not small, and it has never been measured against the rest of the step.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read src/vllm/model_executor/models/glm5_next_kv.cpp:139-170 and 656-682 first, then inspect the mirrored StoreCaches loops at :766 and :795. Run a GLM-5.3-Flash prefill measurement to establish the current cost; done means safely coalescing staging work without violating the single-row synchronization guarantee and validating the result against the baseline.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend, performance
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.