Skip to content

poly, HIP: the expert cache gives VRAM back and grows again on AMD (#13) - #28

Merged
VecSzn merged 2 commits into
simplexai-labs:mainfrom
donschen:amd-expert-cache-13
Oct 17, 2026
Merged

VecSzn merged 2 commits into
simplexai-labs:mainfrom
donschen:amd-expert-cache-13

Conversation

@donschen

Copy link
Copy Markdown
Contributor

On a Linux AMD card the strata-poly expert cache only ever got smaller: each time another program held VRAM for a moment, experts were lost until the server was restarted. This fixes that (#13).

Measured on an RX 9070 XT (16 GB, gfx1201), Ryzen 7 9800X3D, 30 GB RAM, Fedora, ROCm 7 from AMD's wheels, Tiel Coder 35B-A3B UD-Q4_K_XL at 131072 tokens with --kv-whole.

Three causes

  • --kv-whole turned VMM off for the whole engine. Setup writes this flag for AMD, so the expert cache was one allocation. When VRAM ran short it was refilled smaller, and it never grew. VMM now stays on for the expert cache, and --kv-whole keeps the attention caches as one allocation each (kv_alloc()).
  • ROCm returns a released handle's VRAM only when its addresses are freed, and only frees a whole reservation. Unmapping and releasing 320 MiB left only 32 MiB more free; freeing the addresses returned all of it. A partial hipMemAddressFree fails. vmm_unmap() now frees the reservation, reserves it again at the same address, and maps the kept pieces back with their contents intact. This is guarded by STRATA_VMM_RELEASE_WITH_ADDRESSES, defined in hip_compat/cuda.h.
  • hipMemGetInfo's free figure is wrong on Linux. It comes from KFD accounting, which read 924 MiB while amdgpu had 2,128 MiB free; a 1,500 MiB hipMalloc next to the engine still succeeded. At another time it read more than amdgpu had. On Linux the free figure is now amdgpu's mem_info_vram_total minus mem_info_vram_used for the card's PCI address. STRATA_KERNEL_VRAM_FREE=0 keeps hipMemGetInfo's figure.

Result

Another process taking 800 MiB shrank the cache from 4,523 to 4,005 experts, and amdgpu's free VRAM rose by the amount cut. When that process ended, the next request grew the cache back to 4,453 experts.

Speed after the fix:

Experts in VRAM 256-token answer 4.5K-token prompt
Before 4,758 105.7 tok/s 1,941 tok/s
After 4,453 110–128 tok/s about 1,820 tok/s

Testing

  • The HIP build is clean for gfx1201, on both the installed 0.1.44.1 source and this branch.
  • The server runs on the 0.1.44.1 source with this change applied.
  • Not tested:
    • This branch's engine has not been run against a model, and that includes the newer parallel-sequence code. Unbound sequences keep their attention caches in bufs_, and with --kv-whole kv_ stays empty as before.
    • The CUDA build is untested. On CUDA the only change is that --kv-whole no longer turns VMM off for the expert cache.

🤖 Generated with Claude Code

…simplexai-labs#13)

On a Linux AMD card the expert cache only ever got smaller: every program that held VRAM a moment cost experts
until the server was restarted.  Three causes, measured on an RX 9070 XT (16 GB, Fedora, ROCm 7 wheels):

- --kv-whole (which setup writes for AMD) turned VMM off for the whole engine, so the expert cache was one
  allocation, filled again smaller when VRAM ran short and never grown.  VMM now stays on for the expert cache;
  --kv-whole keeps the attention caches as one allocation each (kv_alloc()).
- ROCm gives a released handle's VRAM back only when the addresses it was mapped at are freed, and frees only a
  whole reservation: 320 MiB unmapped and released left 32 MiB more free, the addresses freed all of it, and a
  partial hipMemAddressFree fails.  vmm_unmap() frees the reservation, reserves it again at the same address and
  maps the kept pieces back (their bytes stay), under STRATA_VMM_RELEASE_WITH_ADDRESSES (hip_compat/cuda.h).
- hipMemGetInfo's free figure is KFD accounting, which read 924 MiB while amdgpu had 2,128 MiB free (a 1,500 MiB
  hipMalloc beside the engine succeeded), and at another time more than amdgpu had.  On Linux the free figure is
  now amdgpu's mem_info_vram_total less mem_info_vram_used for the card's PCI address; STRATA_KERNEL_VRAM_FREE=0
  keeps hipMemGetInfo's.

With Tiel Coder UD-Q4_K_XL at 128K: another process taking 800 MiB shrank the cache 4523 -> 4005 experts and
amdgpu's free VRAM rose by what was cut; when it ended, the next request grew the cache to 4453.  A 256-token
answer at 110-128 tokens/s and a 4.5K prompt at about 1,820 tokens/s (105.7 and 1,941 before, with 4,758 experts
cached).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@donschen

Copy link
Copy Markdown
Contributor Author

Related to #7: this is the hipMemGetInfo / mind_vram() finding requested there, root-caused on an RX 9070 XT (KFD's free figure, ROCm not freeing released VMM handles until the reservation is freed, and --kv-whole turning VMM off). #7 disables shrinking on HIP; this keeps the cache elastic instead.

- An integrated GPU keeps hipMemGetInfo's free figure: amdgpu's mem_info_vram files count only its BIOS carve-out
  (a Ryzen 7 9800X3D's Radeon, gfx1036: 512 MiB) while HIP gives it system RAM (15,536 MiB free), so an APU would
  have lost almost its whole expert cache.
- A step of vmm_unmap()'s rebind that fails once the kept pieces have left their addresses ends the engine with a
  message instead of throwing: the throw reached the request's catch in main.cpp and the next request read VRAM
  that was no longer mapped.  The server starts the engine again for the next request.
- vmm_unmap() waits until amdgpu counts the VRAM it gave back as free (2 to 5 ms, at most 100 ms): the log line
  after a shrink read 248 MiB left where there were 1,012.
- The rebind is Linux only, where it was measured; Windows' HIP runtime keeps the plain release.

RX 9070 XT, Tiel Coder UD-Q4_K_XL: 800 MiB taken by another process shrank the cache 4694 -> 4180 experts with
1012 MiB left (amdgpu: 1013), and it grew to 4623 when that process ended.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@VecSzn
VecSzn merged commit dbed2ee into simplexai-labs:main Oct 17, 2026
@VecSzn

VecSzn commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator

Merged, thanks, and thanks for tracking down all three causes. The CUDA side only changes under --kv-whole, and --check-batch with three sequences matched every token with and without it on an RTX 4070. I couldn't run it on an AMD card, so reports from other AMD users built from main, or on the next release, are welcome here or in #13.

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