Skip to content

fix(llm): serialize CUDA embed contexts on WSL like Windows - #984

Open
Phantomn wants to merge 1 commit into
tobi:mainfrom
Phantomn:fix/wsl-cuda-parallelism
Open

Phantomn wants to merge 1 commit into
tobi:mainfrom
Phantomn:fix/wsl-cuda-parallelism

Conversation

@Phantomn

Copy link
Copy Markdown

Problem

On WSL2 with CUDA, qmd embed sometimes aborts with exit 134:

ggml-cuda.cu:106: CUDA error
CUDA error: out of memory
  current device: 0, in function alloc at ggml-cuda.cu:593
  cuMemAddressReserve(&pool_addr, CUDA_POOL_VMM_MAX_SIZE, 0, 0, 0)

This happens with plenty of VRAM free (RTX 4060 Ti 8 GB, about 6.8 GB free, embeddinggemma-300M).

Root cause

resolveSafeParallelism already serializes CUDA to one context on Windows because of #519. It only checks process.platform === "win32". WSL reports linux, but its CUDA runs on the Windows GPU driver: nvidia-smi inside WSL shows the Windows driver version. So WSL gets the full computed pool (8 contexts here), and the same class of multi-context CUDA failure appears.

Change

  • Move isWSL() from store.ts to llm.ts and export it. store.ts already imports from llm.ts, so this avoids an import cycle and keeps one WSL detector.
  • resolveSafeParallelism: treat WSL + CUDA like Windows + CUDA and default to 1 context. Vulkan and CPU are unchanged. QMD_EMBED_PARALLELISM still overrides it.
  • Add an optional wsl option so tests are deterministic on any host.
  • Add a CHANGELOG entry under Unreleased.

Evidence (WSL2 Ubuntu 24.04, driver 616.92, qmd 2.8.3)

A single ~1700-chunk document, re-embedded repeatedly with other CUDA processes holding the GPU:

QMD_EMBED_PARALLELISM runs aborts
8 (the auto value on this card) 7 1, identical cuMemAddressReserve abort
1 7 0

The same abort also hit 2 real qmd embed runs before the investigation. Cost of serializing: 31s → 42s for that document.

Ruled out along the way: real VRAM exhaustion (about 6.8 GB free), other processes holding CUDA (embed still succeeded with 5 holders), and address-space leaks from context create/dispose cycles (VmSize flat over 40 cycles).

Tests

  • test/llm.test.ts: the new WSL case fails on main and passes here.
  • Full vitest run test/: 1247 passed, 79 skipped. Run with WSL_DISTRO_NAME/WSL_INTEROP unset, as CI does.
  • tsc -p tsconfig.build.json --noEmit and oxlint are clean.

Note: 4 Git Bash tests in test/store-paths.test.ts fail when run inside WSL on both main and this branch, because isWSL() is true there. That behavior is pre-existing and unchanged.

Related: #519, #640

🤖 Generated with Claude Code

@Phantomn
Phantomn force-pushed the fix/wsl-cuda-parallelism branch from 1dbbed4 to ec07ffd Compare October 8, 2026 13:00
@Phantomn

Copy link
Copy Markdown
Author

Friendly ping: main still gates the CUDA one-context default on win32 only (resolveSafeParallelism in src/llm.ts), so WSL2 + CUDA users can still hit the intermittent cuMemAddressReserve abort. This PR is rebased-clean against current main. Happy to rebase, split, or gather more logs if that helps review.

WSL reports process.platform "linux" but its CUDA runs on the Windows
GPU driver, so the Windows-CUDA one-context default from tobi#519 did not
apply there. With the computed pool (8 contexts on an 8 GB card) qmd embed
aborted intermittently in cuMemAddressReserve (ggml-cuda.cu:106, out of
memory) with plenty of VRAM free.

Move isWSL() from store.ts to llm.ts (store.ts already imports llm.ts)
and treat WSL CUDA like Windows CUDA. QMD_EMBED_PARALLELISM still wins.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Phantomn
Phantomn force-pushed the fix/wsl-cuda-parallelism branch from ec07ffd to 5045955 Compare October 28, 2026 02:15
@Phantomn

Copy link
Copy Markdown
Author

Rebased onto current main (86d2365, after #1072) — no conflicts. Re-checked against the stricter type-check that #1072 added:

  • tsc -p tsconfig.json --noEmit (whole project, tests included) and tsc -p tsconfig.build.json --noEmit: clean
  • oxlint: clean
  • test/llm.test.ts: 53 passed under vitest, 53 passed under bun. The new WSL test fails on main and passes on this branch.

The CI and Nix workflows on this PR show action_required, so they haven't run yet. Could you approve them when you get a chance?

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.

1 participant