Documentation
¶
Overview ¶
Package latencykernel implements the blis-schemas Kernel interface: it prices GPU work for one resolved deployment.
Construction resolves everything — catalog facts, registry coefficients, the engine settings a layout overrides, and the model graph flattened to a per-kind plan. After that every method is a pure function of its arguments, so a discrete-event simulator can call StepTime at any simulated instant, from any goroutine, and get the same answer.
Two constructors, for the two shapes a caller comes in. Open takes a scenario filename and the roots its names resolve against, which is what a simulator or a scoring command has. New takes the documents already parsed, which is what a configuration search producing deployment variants that were never written to disk has. Open ends in New, so both validate identically and neither can price a deployment the other would refuse.
Speed is a requirement rather than a nicety: a simulator calls StepTime once per simulated step, millions of times over a run. So the hot path does arithmetic over a short pre-aggregated slice and allocates nothing. The plan holds one entry per distinct layer kind rather than one per layer, which for the deepest model in the catalog is three entries instead of 108.
Index ¶
- Constants
- func LoadBundle(path string) (*scenario.Scenario, *deployment.Deployment, error)
- type Inputs
- type Kernel
- func (k *Kernel) AdmissionOverhead(promptTokens int) time.Duration
- func (k *Kernel) CompletionOverhead() time.Duration
- func (k *Kernel) DecodeContextParallelWidth() int
- func (k *Kernel) Deployment() deployment.Pool
- func (k *Kernel) Evidence() (measured, total int, assumed []string)
- func (k *Kernel) FixedBytes() kernel.MemoryBreakdown
- func (k *Kernel) OutputTokenOverhead() time.Duration
- func (k *Kernel) PDTransferTime(tokens int, from, to kernel.Placement) time.Duration
- func (k *Kernel) PrefillContextParallelWidth() int
- func (k *Kernel) Provenance() []kernel.CoefficientOrigin
- func (k *Kernel) Resolved() kernel.Resolution
- func (k *Kernel) SequenceFixedBytes() int64
- func (k *Kernel) SequenceVariableBytes(tokens int) int64
- func (k *Kernel) StepTime(b kernel.Batch) kernel.StepEstimate
- func (k *Kernel) StepTimeInto(b kernel.Batch, per map[kernel.Resource]time.Duration) kernel.StepEstimate
- func (k *Kernel) TierTime(tier string, dir kernel.Direction, bytes int64, queueDepth int) time.Duration
- type Repos
Constants ¶
const EngineBehaviourVersion = "0.31.0"
EngineBehaviourVersion is the vLLM release whose engine behaviour this kernel encodes.
const KernelAssumptionSet = "blis-latency-kernel"
KernelAssumptionSet is the Set a Provenance entry carries when the kernel supplied the value itself rather than reading it from a registry set or the deployment. No registry set can take this name, since registry sets are named for their coefficient family, so a consumer can tell the two apart without parsing anything else.
Variables ¶
This section is empty.
Functions ¶
func LoadBundle ¶
func LoadBundle(path string) (*scenario.Scenario, *deployment.Deployment, error)
LoadBundle reads a scenario and the deployment applied to it from one file, as two YAML documents separated by `---`.
blis-schemas keeps the two apart: a Scenario is the immutable problem (model, cluster inventory, coefficient and engine-version references) and a Deployment is the tunable configuration chosen against it (pools, each a parallelism layout with its own engine settings). One `blis run` is one of each.
They share a file here, rather than sitting in a scenario and a sibling deployment file, because a measurement row addresses a deployment by a single filename — `{"scenario": "glm-5-h200-fp8-sglang-tp8.yaml", ...}` — and testdata/measurements holds over three thousand such rows. A sibling-file layout would rewrite every one of them to say nothing it does not already say.
blis-schemas' own LoadScenario and LoadDeployment each open a path and read one document, so neither can reach the second half of a pair; this reproduces their strict decoding over a single stream instead. Strictness is the point: a misspelled key that parsed silently would leave a document that validates while omitting the setting its author intended, which is the failure mode hardest to notice.
Types ¶
type Inputs ¶
type Inputs struct {
Scenario *scenario.Scenario
// Deployment is the tunable configuration applied to the scenario: the pools that lay
// the model out. It is a separate document because a scenario fixes the immutable
// problem and an optimizer sweeps deployments against it.
Deployment *deployment.Deployment
// PoolIndex selects which pool of a disaggregated deployment this kernel prices. Each
// pool runs its own engine with its own settings, so one kernel per pool.
PoolIndex int
Model *model.Graph
Chip *hardware.Chip
Fabric *hardware.Fabric
Devices []*hardware.StorageDevice
Coefficients []*coefficient.Set
// Rules carry the engine-version behaviour the resolver needs.
Rules resolve.EngineRules
}
Inputs are the documents a kernel is built from. They arrive already parsed: loading belongs to blis-schemas, and New takes documents rather than paths so a caller holding structs — a config search producing deployment variants never written to disk, a test, a generator — does not have to serialize them first. Open is the path-based half of the pair.
New DOES validate them, at the field layer, which is the half of blis-schemas' two-layer validation that is version-independent and always the author's to fix. See New.
func OpenInputs ¶
OpenInputs resolves one pool of a scenario file into the documents New prices it from, without building the kernel.
It is OpenPool's loading half, exported for a caller that needs the documents as well as the kernel. A scoring harness that configures a rival backend for the same deployment needs the model's name, the chip's memory and the model's expert geometry -- deployment identity, which the kernel deliberately does not re-export (blis-schemas#36 draws that line: identity for comparison is the harness's concern, not the cost model's). Reading them from these Inputs, and building the kernel with New(in), means the harness holds the SAME chip and graph the kernel priced, not a second load of files that could resolve differently.
What it must not be used for is reaching configuration the kernel resolved: widths and backends are Resolved's to report, and the pool's settings are Deployment's. Reading those from Inputs instead would rebuild the second answer to "which pool is this" that the interface exists to remove.
type Kernel ¶
type Kernel struct {
// contains filtered or unexported fields
}
Kernel prices one deployment. Obtain one from New; the zero value is not usable.
func New ¶
New resolves the inputs into a kernel.
Everything that can be decided once is decided here: which coefficients apply, which requested settings the layout overrides, and the model graph flattened to a per-kind plan. The result holds no mutable state, so its methods are pure and safe to call concurrently.
Construction is deliberately strict. A missing coefficient, an unresolvable condition or a non-positive rate is an error rather than a default, because each would otherwise produce a step time that looks plausible and is wrong by whatever the term contributes. validateDocuments extends that strictness to the documents themselves.
func Open ¶
Open resolves one scenario file into a kernel.
The path-based half of the constructor pair. New takes documents a caller already holds; Open takes a filename and the roots its names resolve against, which is what a simulator or a scoring command has. Both end in New, so both inherit its validation and neither can price a deployment the other would reject.
It is exported because the alternative is every consumer reimplementing it. Go forbids importing another module's internal packages, so while this lived in internal/harness, inference-sim carried a copy of the sequence and said so in a comment, and blis-config-search and every future backend repo would have needed their own. The duplication was tolerable only because it was confined to loading — but "which files a scenario implies" is this package's answer to give, not each caller's to guess.
Every artifact is loaded through blis-schemas' own loaders, and the engine rules come from that package's version registry rather than from a caller, so a scenario pinned to an older release cannot silently get current behaviour.
It prices the deployment's FIRST pool, which is what a colocated deployment has. Use OpenPool to price one pool of a disaggregated one.
func OpenPool ¶
OpenPool resolves ONE pool of a scenario file into a kernel.
A disaggregated deployment states a prefill pool and a decode pool, and the two differ in the quantities that set step time: tensor-parallel width, whether expert parallelism is on, the engine's token budget. One kernel prices one pool — each runs its own engine with its own settings — so a caller serving the roles separately opens one kernel per pool. Pricing both from pool 0 would charge the decode pool the prefill pool's parallelism, and the simulation would still run.
Open is this function at pool 0. Both exist because the common case should not have to name an index, and the disaggregated case cannot be served without one.
func (*Kernel) AdmissionOverhead ¶
AdmissionOverhead returns host time before a request can be scheduled.
func (*Kernel) CompletionOverhead ¶
CompletionOverhead returns fixed host time at request completion.
func (*Kernel) DecodeContextParallelWidth ¶
DecodeContextParallelWidth returns how many ranks shard the decode KV cache.
A consumer sizing its own KV budget needs it and cannot derive it from anything else this kernel exposes: it is the only axis that shards a LATENT cache, because KVBytesPerToken floors at one KV head and a latent cache has exactly one, so no tensor-parallel width reduces it. A simulator that read Resolution's TensorParallelWidth alone would size an MLA deployment's cache as if DCP did nothing.
From the resolved layout rather than the deployment document: a Parallelism states a request and resolution settles it.
A method on this type rather than a field of kernel.Resolution only because blis-schemas v0.2.2's Resolution carries the tensor-, data- and expert-parallel widths and not the two context-parallel ones. The argument that put those three in Resolution -- a consumer that can read one width and not the others has to re-derive the rest -- applies to these two as well, so this method and its companion are interim, pending the same fields upstream.
func (*Kernel) Deployment ¶
func (k *Kernel) Deployment() deployment.Pool
Deployment returns the pool this kernel prices, as the document stated it.
The request, not the resolution: a caller wanting a width or a backend reads Resolved, and reads this for the settings resolution does not touch -- the admission settings a scheduler sizes itself from. It is the pool New was given at PoolIndex, so a caller holding several kernels of one disaggregated deployment needs no index of its own to know which pool each prices.
A deep copy, so a caller writing through one of the engine block's pointer fields changes its copy and not the kernel. blis-schemas permits returning a shallow copy and asking callers not to write; this is not a hot path, and a promise the type enforces is worth more than one a reader has to keep.
func (*Kernel) Evidence ¶
Evidence reports how much of the resolved coefficient set rests on measurement, so a caller can state a prediction's footing.
func (*Kernel) FixedBytes ¶
func (k *Kernel) FixedBytes() kernel.MemoryBreakdown
FixedBytes returns occupancy independent of the request set, per rank.
func (*Kernel) OutputTokenOverhead ¶
OutputTokenOverhead returns host time per emitted token.
func (*Kernel) PDTransferTime ¶
PDTransferTime prices moving one request's KV between pools.
Decode-context parallelism deliberately does NOT divide this, and the omission is the reason kvBytesPerToken is left as a per-token figure rather than being sharded once at construction. A prefill pool holds the whole cache for a request -- DCP shards the DECODE cache, and vLLM's own wording is "Number of ranks that shard the decode KV cache" (vllm/config/parallel.py:359-362 at v0.31.0) -- so the bytes that cross between pools are the whole request's, whatever the decode pool's dcp width is. A divisor here would make every PD transfer exactly dcp times too cheap.
func (*Kernel) PrefillContextParallelWidth ¶
PrefillContextParallelWidth returns how many ranks split a prefill sequence.
The companion to DecodeContextParallelWidth, and the two are genuinely independent: PCP splits prefill computation and expands the process world size while leaving the KV cache replicated, where DCP shards the cache without expanding the world size -- it reuses the tensor-parallel ranks when pcp is 1, and spans the PCP axis or the whole TP x PCP block otherwise (vllm/config/parallel.py:359-362 at v0.31.0). A consumer sizing a deployment needs both, and neither can be derived from the other.
Interim for the same reason as DecodeContextParallelWidth.
func (*Kernel) Provenance ¶
func (k *Kernel) Provenance() []kernel.CoefficientOrigin
Provenance reports every coefficient this kernel used and its evidence.
func (*Kernel) Resolved ¶
func (k *Kernel) Resolved() kernel.Resolution
Resolved reports the configuration after resolution, including overridden requests.
func (*Kernel) SequenceFixedBytes ¶
SequenceFixedBytes returns per-sequence bytes that do not vary with prompt length: recurrent state, where the model has it and the cache mode keeps it bounded.
func (*Kernel) SequenceVariableBytes ¶
SequenceVariableBytes returns per-sequence KV bytes for a token count, quantized to the engine's page size.
func (*Kernel) StepTime ¶
func (k *Kernel) StepTime(b kernel.Batch) kernel.StepEstimate
StepTime prices one forward pass. This is the hot path.
The returned StepEstimate carries a freshly allocated PerResource map. A simulator that calls this per simulated step, millions of times, can use StepTimeInto instead and reuse one map across calls, which makes the path allocation-free.
func (*Kernel) StepTimeInto ¶
func (k *Kernel) StepTimeInto(b kernel.Batch, per map[kernel.Resource]time.Duration) kernel.StepEstimate
StepTimeInto prices one forward pass, writing the per-resource breakdown into per instead of allocating a map.
per is cleared first, so a caller reuses one map for every step. Passing nil is allowed and yields an estimate with no breakdown, for a caller that wants only the duration.
The estimate's own fields are values, so nothing else escapes: this form allocates nothing at all, which is what lets a long simulation avoid the garbage collector on its innermost loop.
type Repos ¶
type Repos struct {
Scenarios string // directory holding scenario files
Catalog string // a blis-catalog checkout, or a pinned copy of one
Registry string // a blis-registry checkout, or a pinned copy of one
}
Repos locates the roots a scenario's names resolve against.
A scenario names its model, hardware, fabric and coefficient sets without saying where they live — it describes a deployment, not a filesystem — so a caller supplies the roots.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
bandprobe
command
Command bandprobe prices one step per line of a CSV and prints both band edges.
|
Command bandprobe prices one step per line of a CSV and prints both band edges. |
|
overlap-probe
command
Command overlap-probe answers one question: does the per-layer overlap term change the SHAPE of the concurrency response, or only its level?
|
Command overlap-probe answers one question: does the per-layer overlap term change the SHAPE of the concurrency response, or only its level? |
|
score
command
Command score reports how well the kernel predicts published measurements.
|
Command score reports how well the kernel predicts published measurements. |
|
shape
command
Command shape scores this kernel's concurrency response against NVIDIA's end-to-end accuracy snapshot, along AISimulate's own published error on the same points.
|
Command shape scores this kernel's concurrency response against NVIDIA's end-to-end accuracy snapshot, along AISimulate's own published error on the same points. |
|
worked-table
command
Command worked-table prints the step-time table the latency-kernel design's §2.1 states, computed by the kernel itself from the committed catalog and registry.
|
Command worked-table prints the step-time table the latency-kernel design's §2.1 states, computed by the kernel itself from the committed catalog and registry. |
|
internal
|
|
|
artifacttest
Package artifacttest holds the rule every test package uses to decide whether a failure to load a catalog or registry artifact is a skip or a failure.
|
Package artifacttest holds the rule every test package uses to decide whether a failure to load a catalog or registry artifact is a skip or a failure. |
|
harness
Package harness holds what this repository's scoring and reporting commands share: opening a kernel from a scenario file, and building the batch shapes a comparison prices.
|
Package harness holds what this repository's scoring and reporting commands share: opening a kernel from a scenario file, and building the batch shapes a comparison prices. |
|
price
Package price holds the cost laws the kernel composes: the GEMM efficiency ramp, the cross-node collective scaling, and the resource maximum.
|
Package price holds the cost laws the kernel composes: the GEMM efficiency ramp, the cross-node collective scaling, and the resource maximum. |
|
resolve
Package resolve turns a scenario, the catalog facts it names and the registry sets it lists into one frozen configuration a kernel prices against.
|
Package resolve turns a scenario, the catalog facts it names and the registry sets it lists into one frozen configuration a kernel prices against. |