Skip to content

SuperSLM 1.11.0

Choose a tag to compare

@dansupergameprogrammer dansupergameprogrammer released this 30 Sep 21:57
· 555 commits to main since this release

SuperSLM 1.11.0

Released 2026-09-30.

SuperSLM 1.11.0 lets a host's parallel-for hook split decode, and a one-token prefill, across
threads. It is opt-in: nothing changes unless the host sets the new bit. Every output is
bit-identical with and without it, at any task count: the same tokens, the same save blobs and the
same axis digest. There is no change to any ABI struct, status ordinal or save format, and no model
artifact needs reconversion.

What changed

1.10.0's parallel-for hook (sslm_parallel_for) was read only by the logits step. In 1.11.0 a host
can set bit 1 of sslm_parallel_for.reserved, the new SSLM_PARALLEL_FOR_MATVEC, to have the CPU
backend also split its one-row projections through the hook's run. These are each decode layer's
q, k + v, o, gate + up and down projections, and the same projections in a prefill call that admits
exactly one token.

  • When a group splits. Output rows are split when the split gives a group two or more tasks.
    That needs max_tasks of at least 2, more than 64 rows, and at least twice 256 KiB of weight bytes.
    The 256 KiB constant caps the task count by the group's weight bytes, so the last task can be
    smaller. docs/api.md gives the exact rule.
  • Why output is unchanged. Every row is still one exact integer sum computed on one thread.
  • Allocation. The split adds no heap allocation.
  • Setters. Both setters accept bit 1 and still refuse every other bit, so a host that sets
    bit 1 against 1.10.0 is refused rather than silently ignored. The GPU setter accepts the bit and
    ignores it.
  • A hook that fails inside a decode layer. If run breaks exactly-once inside a decode layer,
    the call fails with SSLM_INVALID_ARGUMENT. The sequence rests at the start of that layer, with
    that layer's saturation counts put back, and the next call resumes there.
  • Behaviour change for every host, with or without the bit. A prefill or prefix-prefill call
    that fails before admitting a token now leaves the sequence's five saturation counts as they were
    before the call. In 1.10.0 it kept counts that no K/V entry backed.
  • C++ headers. RunLayerLoop (both overloads) and RunLayerLoopChunkBatched gain threaded
    overloads that take a trailing GemmThreading, for the engine's own ABI layer. They are not in
    the SUPERSLM_API export slot, so a shared engine library exports the same symbols as 1.10.0.
    The existing symbols stay and run serially.

Measured speed

Measured on a Zen 2 desktop (Ryzen 9 3950X, Windows, MSVC Release) with the Qwen2.5-0.5B model
(24 layers) and a std::thread pool as the hook. Each figure is the median of 15 interleaved
off/on pairs.

max_tasks decode tok/s, context 16 decode tok/s, context 300
2 25.5 → 33.8 (1.32x) 24.4 → 32.0 (1.31x)
4 26.0 → 39.5 (1.52x) 25.0 → 37.1 (1.48x)
8 24.9 → 35.2 (1.41x) 25.0 → 35.4 (1.42x)
  • One-token prompt at 4 tasks: 33.07 → 20.31 ms per token (1.63x).
  • Task-size constant: a sweep of 64 KiB, 256 KiB and 1 MiB at 4 tasks kept 256 KiB.
  • Under memory contention (a 4-thread, 4 GB streaming-read competitor running alongside):
    decode at context 300 goes from 12.0 to 21.8 tok/s (1.82x). The competitor's own bandwidth falls
    more while threaded decode runs: to 18.1 GB/s, from 29.7 GB/s alone, against 23.6 GB/s beside
    serial decode.

Threaded decode is therefore faster, but it takes more memory bandwidth from other memory-heavy
work on the same machine. A game should measure it in its own frame before turning it on. The
SuperSLM-Unreal plugin ships with it off until it has been measured in game.

Compatibility and required action

None for a host that does not set the bit, with one exception: the saturation-count behaviour
change above, which applies to every host. The C ABI verbs, struct layouts, status ordinals and
save formats are unchanged. A host that wants threaded decode must set bit 1 and pass a
sslm_parallel_for whose run calls each task exactly once. A host can test for the feature at
compile time by checking whether SSLM_PARALLEL_FOR_MATVEC is defined.

Verification

  • Bit-identity:
    • The axis digest's GLOBAL hash is f740f833…, unchanged from 1.10.0, on all ten Linux legs:
      auto and forced scalar/SSE2/AVX2/AVX-512, each on GCC 13.3 and Clang 18.1.
    • On the real Qwen2.5-0.5B, 64 greedy tokens and the final save blob are byte-equal to a run with
      no hook, at max_tasks 1, 2, 3, 4 and 8. That run makes 97 run calls per decode token at 2
      tasks and above.
    • Against a v1.9.0 library, a threaded save blob equals v1.9.0's blob, and each library re-saves
      the other's blob byte for byte (Linux and Windows).
  • Mutants: 23 of 23 killed.
  • Found by the release machine's build.bat and fixed before release: a default
    BUILD_TESTING=OFF build failed, because a new test target was built unconditionally
    (e46c0f1). The three new overloads were also in the SUPERSLM_API export slot, which the
    export-slot check (X2) caught; they were taken out of it. A floating-point scan test had been
    moved to 18 objects, but the archive it reads is a fixed, pre-built one that still has 17; it
    was put back.
  • Code review: SHIP on a Zen 2 desktop (Windows, MSVC), after a confirmation re-check of four
    fixes. Windows suites: 133,931 checks, 0 failures; the forced SSE2 and AVX2 suites are green.
  • GitHub CI: green on the merged head d31c65f, 78 of 78 checks.
  • Release machine (Zen 2 desktop, Windows, MSVC 19.33, RTX 2080 SUPER; AVX2, no AVX-512):
    • At d200cf9, CMake Release with the GPU on, built fresh, with this machine's decode-threading
      fixtures: superslm_tests 133,931 checks, 0 failures, including the decode-threading cells
      (2,152 checks). The commits after it change build files, export attributes, tests and notes,
      not engine arithmetic.
    • At 445e206, build.bat runs to completion. Every gate passes: the CPU-only default build,
      the export-slot check (63 of 63), the floating-point scan suite (231 passed), and its own
      superslm_tests step (129,093 checks, 0 failures).
    • The release gate's tests pass, 4 of 4, after its two compiled diagnostics were rebuilt from
      445e206 and re-pinned.
    • The decode figures above were measured on this machine.

Files changed

  • src/forward/parallel_split.cpp, src/forward/parallel_split.h: the one-row split runner.
  • src/forward/forward_sites.cpp, include/superslm/forward_sites.h: the split call sites and the
    threaded overloads.
  • include/superslm/parallel_for.h, src/sslm_abi.cpp, include/superslm/sslm_abi_functions.inc,
    src/gpu/gpu_1p0.cpp, include/superslm/gpu_1p0.h: bit 1 in both setters.
  • include/superslm/checked_chain_funnel.h, include/superslm/gpu_layer_loop_guards.def: comments
    and line citations.
  • tools/gen_decode_threading_fixture.py and the decode-threading tests: the fixtures and cells.
  • tools/build_adapter_dump.bat and 26 other MSVC build lists: they now include
    parallel_split.cpp.
  • CMakeLists.txt, tests/install-consumer/CMakeLists.txt, README.md, CHANGELOG.md, docs/api.md
    and this note: version 1.11.0.
  • tests/data/t2704_release_manifest.json, tools/test_t2704_release_gate.py: the release gate's
    compiled diagnostics, re-pinned to 445e206.