Skip to content

Research: KVarN V-store per-call latency grows with n_kv at fixed grid, K stays flat #163

Description

@raufaser

Research Stage

  • Background Research (Let's try to avoid reinventing the wheel)
  • Hypothesis Formed (How do you think this will work and it's effect?)
  • Strategy / Implementation Forming
  • Analysis of results
  • Debrief / Documentation (So people in the future can learn from us)

Previous existing literature and research

Hi,

I have been chasing a scaling oddity in the KVarN store path on my box (7900 XTX,
gfx1100) and I am out of ideas for what to measure next, so I figured I would ask.

Setup: 27B dense model (D256, GQA 6, 24/4 heads), kvarn5 for K and V, prefill with
ub 512. Timing comes from the built in GGML_KVARN_PROFILE event pairs with
GGML_CUDA_DISABLE_GRAPHS=1.

For reference, q8_0 on the same binary and box does 912 / 835 / 712 t/s at
8k / 16k / 32k, i.e. minus 22% over the range where kvarn5 loses 32%
(550 / 475 / 376 t/s). The gap widens with context (0.60x down to 0.53x of q8_0),
so this looks KVarN specific rather than general long context scaling.

pp q8_0 q5_0 kvarn5 kvarn5 / q8_0
8192 911.95 t/s 907.39 t/s 549.58 t/s 0.60x
16384 834.50 t/s 832.27 t/s 475.26 t/s 0.57x
32768 711.97 t/s 710.26 t/s 376.10 t/s 0.53x

What I see: V store_low per-call mean climbs all the way from 8k to 65k context
at a fixed launch grid (4 blocks). K store over the same range is flat.

ctx 8k 16k 24.6k 32k 49k 65k
V mean/call 36.5 ms 44.1 51.6 59.1 74.3 90.0 ms
K mean/call ~7.7 ms flat throughout

The V curve is smooth (about +0.94 ms per 1k tokens, exponent around 1.6), not
stepwise. At 32k, V totals are 121 s vs 15.9 s for K, so about 7.6x apart with
the same grid shape.

Things I ruled out on my side:

  • Route is stable prompt-generic-mma / compact-tail from 512 to 65536 (route debug env).
  • --flash-attn off gives byte identical V totals (121.1 vs 121.0 s at 32k), so it is not queued FA work on the stream.
  • Host side live_stage_groups planning is 11.8 ms total over an 87 s prefill, so not the host loop.
  • kvarn2/5/8 all show the same exponent despite 4x record bytes, so not payload size.

Two more discriminators since:

  1. Ub sweep at fixed 32k. K behaves normally (totals fall monotonically as ub grows,
    mean linear in ub). V totals are U shaped with the minimum at the default ub 512,
    and the same U shape shows at 8k. So V per-chunk cost is superlinear in ub at any
    fixed position, while K is linear.
ub 128 256 512 1024 2048
K total @32k 17.51 s 16.55 15.76 14.34 12.99 s
V total @32k 137.73 s 123.04 121.20 129.12 141.42 s
V total @8k 23.10 s (not run) 18.74 (not run) 21.58 s
  1. Asymmetric bit widths at 32k/ub512. The gap follows the side, not the bits. Each
    side only cares about its own bits, and V moves all of 0.7% going from 2 to 8 bits.
cfg K mean V mean V/K gap
kvarn5 (sym) 7.70 ms 59.18 ms 7.7x
kvarn2 (sym) 7.27 ms 57.36 ms 7.9x
kvarn8 (sym) 8.43 ms 57.09 ms 6.8x
K8V2 8.30 ms 57.33 ms 6.9x
K2V8 7.29 ms 57.73 ms 7.9x

I also checked the store call site (llama-kv-cache-kvarn.cpp, store()): same
sinkhorn iters (16), same stage/tail groups, same slices (2), same grid for K and V.
The only difference is the value flag. So as far as I can tell, the level gap, the
position slope, and the ub superlinearity are all properties of the value=true path,
i.e. the transposed stage reads in kvarn_quantize_stage_lowshmem via
kvarn_stage_rotated_value (token = value ? row : col).

My question: what in that path could grow with cache position and with ub at fixed
per-flush work? The flush kernel reads O(128) stage data and writes one fixed size
record, so I would expect constant cost per flush, but per-flush cost goes from
9.2 ms at 8k to 14.8 ms at 32k (ub512). Is there something striding over a structure
that grows with n_kv that I am not seeing, or an interaction with the record sweep /
L2 at large ub? Is there a debug counter worth adding, or a knob I can use to test
this without rebuilding?

Happy to run anything you suggest on this box and report back. Raw numbers and logs
available on request.

Hypothesis

No response

Implementation

No response

Analysis

No response

Relevant log output

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions