Skip to content

Add optional expert bundle sidecar for contiguous MoE expert reads #491

Description

@GiorgioOppo

Summary

This proposal adds an optional .expertbundle sidecar for SSD-streamed MoE
models.

The GGUF file remains unchanged. The sidecar is a derived cache where each routed
expert's tensors are stored contiguously:

GGUF layout, simplified:

[gate_expert_0][gate_expert_1]...[gate_expert_N]
[up_expert_0]  [up_expert_1]  ...[up_expert_N]
[down_expert_0][down_expert_1]...[down_expert_N]

expert bundle layout:

[expert_0: gate up down]
[expert_1: gate up down]
...
[expert_N: gate up down]


When the router selects an expert and that expert is not already resident in the
expert cache, the engine can satisfy the miss with one large sequential read
instead of three scattered reads.
The model weights, quantization, and math do not change. This is only a disk
layout optimization for routed expert slabs.
Motivation
In SSD streaming mode, decode can become dominated by routed expert I/O. For
each selected expert, the engine needs the gate, up, and down projections. In
the GGUF layout those slabs are grouped by tensor kind, so loading one expert
requires multiple non-contiguous reads.
A sidecar organized per expert makes a cache miss look like:
pread(bundle_fd, dst, expert_total_bytes, expert_offset);
instead of:
pread(model_fd, gate_dst, gate_bytes, gate_offset);
pread(model_fd, up_dst,   up_bytes,   up_offset);
pread(model_fd, down_dst, down_bytes, down_offset);
This should improve the cold-miss path while preserving the existing GGUF format
and all current decode behavior.
Proposed behavior
The feature is opt-in, for example:
DS4_EXPERT_BUNDLE=1 ./ds4 model.gguf
On startup, if enabled, the engine looks for:
model.gguf.expertbundle
If the sidecar exists and matches the source GGUF, it is opened and used for
expert cache misses / expert gathers.

If the sidecar does not exist, the engine may build it once by reading the
routed expert tensors from the GGUF and writing them in per-expert order.

If there is not enough disk space, or sidecar creation fails, the engine falls
back to the current GGUF gather path.

Numerics must be identical, because the sidecar contains the same expert bytes,
only reordered.

Sidecar format sketch
A minimal header could be:
struct ds4_expert_bundle_header {
    char magic[16];           // "DS4EXPBUNDLE"
    uint32_t version;
    uint32_t n_layers;
    uint32_t n_experts;
    uint32_t n_parts;         // usually 3: gate, up, down
    uint64_t source_size;
    uint64_t source_mtime;
    uint8_t source_hash[32];  // optional but preferred
};
Then a table of entries:
struct ds4_expert_bundle_entry {
    uint32_t layer;
    uint32_t expert;
    uint64_t offset;
    uint64_t gate_bytes;
    uint64_t up_bytes;
    uint64_t down_bytes;
};
The data region stores:
layer 0 expert 0: gate bytes, up bytes, down bytes
layer 0 expert 1: gate bytes, up bytes, down bytes
...
layer L expert E: gate bytes, up bytes, down bytes
The exact metadata can be adjusted to match ds4's current tensor descriptors and
mixed-quantization handling. The important invariant is that one expert maps to
one contiguous byte range.
Validation
The sidecar should be ignored or rebuilt if:
source model size differs;
source model timestamp differs;
source hash / GGUF fingerprint differs;
layer count or expert count differs;
tensor byte sizes do not match the current model metadata;
sidecar version is unsupported.
The safe fallback is always the existing GGUF path.
Expected benefits
This does not reduce the number of expert bytes read. It reduces the number and
randomness of reads on cache misses.
Expected improvements are workload- and disk-dependent, but the optimization is
most useful when:
SSD streaming is active;
expert cache hit-rate is not high enough to hide misses;
the model is too large for RAM;
decode is gather/I/O-bound;
the platform has high sequential NVMe bandwidth but scattered reads are costly.
Tradeoffs
Requires extra disk space, potentially tens of GB for large MoE models.
First run may spend time building the sidecar.
Needs invalidation logic to avoid stale sidecars.
Does not help if expert cache hit-rate is already very high.
Does not change quality or model output.
Why a sidecar instead of changing GGUF?
Changing GGUF layout would make this a model distribution problem. A sidecar keeps
the optimization local and optional:
existing GGUFs continue to work;
no conversion is required for users who do not need it;
users can delete the sidecar at any time;
the engine can fall back automatically.
Implementation notes
The change can be kept narrow:
Add optional sidecar open/build code during model load.
Add a lookup table from (layer, expert) to sidecar offset/size.
In the expert gather/cache-miss path, if the sidecar is available, read the
contiguous expert block.
Otherwise use the existing GGUF tensor-offset path.
Add diagnostic logging reporting whether the bundle is used, built, skipped,
or invalidated.
Example log lines:
expert bundle: found model.gguf.expertbundle, using contiguous expert reads
expert bundle: missing, building sidecar...
expert bundle: skipped, not enough free disk space
expert bundle: invalid source fingerprint, falling back to GGUF
Correctness
This should be a performance-only change. The output must remain identical to the
current GGUF gather path when the same bytes are copied into the same expert
buffers.
Suggested tests:
create a tiny synthetic GGUF-like expert layout;
build a bundle;
verify every (layer, expert) gate/up/down byte range matches the original;
run decode with and without the bundle and compare logits on a fixed prompt;
verify fallback behavior when the sidecar is missing or invalid.

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