Files

22 KiB
Raw Permalink Blame History

Capacity estimation

Add an optional capacity section to an existing cookbook model manifest, reuse shared component and hardware entries, then validate and preview the result.

Walkthrough: FastWan

The FastWan manifest already declares its serving baseline and launch environment. Keep its title and deployments; append these capacity facts:

capacity:
  memory_rules: wan
  components:
    transformer:
      ref: fastwan2.1-t2v-1.3b-vsa-dit
    text_encoder:
      ref: umt5-xxl-encoder
    vae:
      ref: wan2.1-vae-decoder
  reference_runs: []

memory_rules selects a calculator family that can serve several models; wan alone does not establish coverage of FastWan's VSA execution. Reuse entries whose architecture and loaded subset match, or add entries to these shared files:

File Contents
hardware.yaml Specific hardware variants and nominal capacity
transformers.yaml Transformer counts and geometry, including FastWan's VSA gate weights
encoders.yaml Encoder counts and loaded subsets
vaes.yaml VAE counts, loaded subsets, and geometry

The catalog derives id: fastwan21 from the manifest key, name from its title, and model_id from the baseline's generator.model_path. Every deployment becomes an example, preserving its ID, defaults configuration, and env. Do not duplicate id, name, model_id, or examples inside capacity. Models without capacity remain valid cookbook entries. Omit reference_runs or leave it empty until you have successful-run evidence.

From the repository root, use the Node version in the cookbook contributor guide:

npm ci --prefix docs
node docs/js/build-cookbook-capacity.mjs --check
node docs/js/build-cookbook-capacity.mjs --preview fastwan21

The check currently prints Validated 1 capacity model(s) and 50 hardware variant(s). The preview prints the model with component references expanded and each example's baseline under base_config. Inspect its identity, counts, geometry, and evidence. Both commands validate deployments and capacity metadata without writing files, downloading weights, or starting FastVideo; no GPU is needed. Unknown fields, missing/wrong-type references, invalid counts, and unused shared components fail validation.

npm run build:catalog --prefix docs exports index.json, hardware.json, and models/fastwan21.json under docs/assets/cookbook-config/capacity/. Commit authored sources; generated assets are build output and must not be hand-edited.

Authoring reference

Record Required fields Optional fields
Model capacity memory_rules, nonempty components sources, reference_runs
components.<role> ref alternatives (other registered refs)
Shared component name, parameter_count, sources block_count + block_parameter_count, loaded_subset, geometry; VAE-only decode_feature_multiplier
Hardware variant name, vendor, kind, memory, backend, sources aliases, family + variant, form_factor, gpu_count, host_memory, partitionable, partitions, supported_dtypes, notes

Roles are transformer, transformer_2, text_encoder, text_encoder_2, image_encoder, vae, or audio_vae; refs must match the role's component type. Alternatives are documented compatible choices; catalog resolution does not select one or change the serving configuration.

Count the module actually loaded, such as an encoder-only text model or a decoder-only VAE. Describe partial loading in loaded_subset, and count shared or tied storage once. Supply both block fields together; block parameters must not exceed the total. Count fields accept separators such as 1_489_821_760, normalized to numbers during the build; scalar geometry dimensions require ordinary integers. Do not copy component counts into individual model manifests.

Geometry belongs to the shared component. For memory_rules: wan, transformer geometry requires hidden_size, ffn_size, and patch_size (three dimensions); VAE geometry requires temporal_compression, spatial_compression, and vae_decoder_channels, including for alternatives. Shared definitions contain no selected weight/activation dtype or offload settings: those depend on execution. Quantized storage also needs accounting beyond a parameter count.

Optional activation geometry supplies additional facts for future memory rules:

For the initial encoder inventory, keep parameter/block counts, the loaded subset, and sources. The shipped UMT5 entry omits detailed activation geometry. Add encoder geometry when a memory rule needs to model those activations explicitly; it is not required for the initial inventory. Encoder weights still contribute to stage residency. A coarse activation allowance must be declared and calibrated rather than treating missing geometry as zero activation memory.

Component Additional allowed fields Meaning
Transformer and encoder gated_ffn Boolean indicating a gated FFN; separate from attention gating
Encoder hidden_size, ffn_size, num_attention_heads, attention_head_dim Positive integer dimensions for encoder activations
VAE latent_channels, output_channels Positive integer channel counts for the compressed video and final decoded frames

The geometry allowlist also includes transformer num_attention_heads and attention_head_dim. patch_size is exactly three positive integers; gated_ffn is a boolean. Optional fields can be omitted; unknown fields and arbitrary nested geometry mappings are rejected.

The compression ratios determine the latent grid; latent_channels: 16 sizes the compressed video buffer, and output_channels: 3 sizes the final RGB frames. Channels multiply buffer storage, not the number of video tokens. Decoder width scales the rough decode-memory estimate.

Inputs for VAE working memory

VAE working memory can be a large part of the generation peak. Supply enough information to account for all of its parts:

Part Inputs
Compressed video Batch, video dimensions, compression ratios, and latent channels
Feature/cache allowance Optional decode_feature_multiplier, decoder width, batch, spatial extent, and actual activation dtype
Other temporary features and workspace Actual spatial/time chunk sizes, dtype, and a model-specific memory rule
Decoded frames and merge/cast buffers Batch, final output channels, video dimensions, retained copies, dtype, and GPU/CPU placement

Sum buffers that are alive together to estimate the decode peak, then add the weights still on that GPU. An approximate feature/cache allowance alone does not establish the whole working-memory peak; a reviewed rule must account for the remaining allocations without double counting.

Larger frames increase image-sized buffers. More frames increase retained video buffers, but a fixed history cache does not keep growing after it fills up. If finished chunks move to the CPU and their GPU storage is released, GPU output memory behaves differently. Do not infer buffer placement from an API option such as return_frames.

VAE decode feature multiplier

The optional VAE-only top-level field decode_feature_multiplier is a positive finite number. The Wan2.1 entry supplies 48, an uncalibrated heuristic borrowed from the calculator proposal in PR #1978:

decode_feature_multiplier: 48

It represents an equivalent allowance for internal decoder feature/cache buffers, not 48 RGB frames and not a value from the architecture configuration. A future rule can use:

feature_cache_bytes ≈ decode_feature_multiplier * batch * vae_decoder_channels
                    * height * width * activation_bytes

For batch 1, decoder width 96, output size 480 × 832, and two-byte BF16 activations, this gives about 3.43 GiB for the feature/cache allowance only. It is not a total decode or generation peak. Actual cache mode, dtype, spatial and temporal tiling, and buffer lifetimes affect whether this approximation applies; validate and calibrate it for the execution being modeled.

Decoded output and merge/cast buffers are separate. For example, PR #1978 uses an additional term equivalent to:

output_buffer_bytes ≈ batch * frames * output_channels * height * width
                    * (2 * activation_bytes + 4)

For RGB output, this assumes decoded frames are retained twice at decode dtype and once as FP32. Retention, placement, and execution paths can differ; this is not a universal guarantee. Resident weights and other workspace are additional terms in the peak. The builder exports the authored multiplier without calculating memory; omission remains omission, with no automatic 48 fallback.

Execution and hardware facts

Tile sizes/strides, feature-cache choices, dtypes, and offload are execution settings. Keep them in the native serving configuration, where applicable, rather than component geometry. For example, generator.pipeline.vae_tiling is preserved in an exported example's base_config; that does not prove it changes the selected decoder path. A future estimator must resolve runtime defaults and effective behavior before applying a tiling rule. VAE tiling changes the decode chunks and their working memory, not the full transformer's video-token count. Component geometry is not a complete layer schedule, cache/workspace model, or GPU-fit calculation.

For example, Wan accepts these literal dotted keys under experimental:

generator:
  pipeline:
    vae_tiling: true
    experimental:
      "vae_config.use_feature_cache": false
      "vae_config.tile_sample_min_height": 512
      "vae_config.tile_sample_min_width": 512
      "vae_config.tile_sample_stride_height": 384
      "vae_config.tile_sample_stride_width": 384

This selects spatial tiles of up to 512 by 512 output pixels, with a 384-pixel stride and 128-pixel overlap. With compression 8, that is up to 64 by 64 latent positions and a 48-position stride. Edge tiles can be smaller.

Disabling feature caching lets this Wan path reach spatial tiling; the default cached path bypasses it. This example does not enable temporal tiling, so each spatial tile still contains the full clip in time. The estimate must use that actual extent and include retained tile outputs and merge buffers. Do not apply a fixed memory discount from vae_tiling: true, or assume this mode uses less memory than the default cached decoder. Other model families can use different tiling controls. This setting flow was checked in code, not by GPU generation.

Hardware IDs describe variants such as H100 SXM 80 GB. The authored memory.nominal preserves GB or GiB; the build exports normalized decimal memory_gb (1 GB = 1e9 bytes), converted to GiB with memory_gb * 1e9 / 1024**3. Use memory.type: dedicated for discrete VRAM or unified for capacity shared with the CPU. Neither means measured free memory. supported_dtypes describes hardware format capability, not FastVideo kernel support; omission means unknown. aliases connect a variant to existing UI IDs and search names.

Use checkpoint model_index.json/component config.json files, tensor headers or indexes, and the FastVideo loader to establish composition, counts, and geometry. An index's total bytes are insufficient to count mixed/quantized weights. Prefer one pinned checkpoint-folder link covering config and weights, plus an implementation link when it explains loading or architecture behavior. Keep model-specific checkpoint references in the model manifest instead of repeating them for each shared component. Similar model names do not establish that components can be shared. Offline scripts or agents may prepare candidate metadata for review; builds and browser loaders do not run them, import model code, or download checkpoints.

Hardware catalog

hardware.yaml is the authored lookup catalog. It records device specifications, not a runtime support matrix: a searchable variant can still be unsupported by a model, backend, or kernel. Every entry requires a vendor source URL and the date it was checked. The coverage checklist tracks what is present, what is next, and what is deliberately excluded. The catalog is for discovery and reuse, not a prerequisite for running a model: a missing variant uses the custom-device path, and unknown means no estimate, not that FastVideo cannot attempt the recipe.

h100-sxm-80gb:
  name: NVIDIA H100 SXM 80 GB
  vendor: nvidia
  kind: gpu
  family: nvidia-h100
  variant: SXM 80 GB
  form_factor: sxm
  memory:
    type: dedicated
    nominal: { value: 80, unit: GB }
  backend: cuda
  partitionable: true
  partitions:
    - id: 1g.10gb
      label: 1g.10gb
      memory: { value: 10, unit: GB }
  supported_dtypes: [fp32, fp16, bf16, fp8]
  aliases: [h100]
  sources:
    - url: https://www.nvidia.com/en-us/data-center/h100/
      checked: 2026-10-19

memory.type is dedicated or unified. memory.nominal preserves the vendor's value and unit (GB or GiB). Unified memory is shared system memory, not VRAM; do not describe it as a GPU-only allocation. Add memory.usable plus a memory.policy only when a documented policy derives an application budget. Without that policy the estimator uses a labeled 75% application default (DEFAULT_UNIFIED_BUDGET_FRACTION); memoryBudgetGiB overrides it. Nominal unified memory is never treated as available VRAM, and the assumption is reported in memory.budgetSource and the result notes.

family groups exact variant records, so board, form-factor, and capacity differences stay separate entries. partitions list selectable MIG/vGPU-style allocations; selecting one never silently inherits full-card capacity. gpu_count describes the device record and does not authorize summing GPUs into one pool. host_memory is host RAM for CPU offload, separate from dedicated VRAM and from unified memory. aliases preserve old links and search names; canonical IDs stay stable.

Custom devices use the same memory fields and must declare backend and supported_dtypes. They are not catalog-verified:

const custom = {
  id: "custom",
  name: "Custom accelerator",
  custom: true,
  memory: { type: "dedicated", nominal: { value: 48, unit: "GB" } },
  backend: "cuda",
  supported_dtypes: ["fp32", "fp16", "bf16", "fp8"],
};
const result = estimateMemory({
  config,
  workload,
  model,
  hardware: custom,
  memoryBudgetGiB: 40, // optional application cap, in GiB
  partitionId: "1g.10gb", // optional catalog partition
  allocationMemoryGb: 10, // optional provisioned allocation, in GB
  hostMemoryGb: 64, // optional host RAM for offload
});

An unknown backend or missing memory type/capacity returns unknown. A missing format list is accepted for fp32/fp16/bf16 storage and called out as an assumption; fp8 or NVFP4 storage still returns unknown because capability was not cataloged. The estimate reports memory (type, nominal, budget, partition) and host (required, capacity, status) separately from capacityGiB and status.

The generated hardware.json is versioned:

{ "version": 1, "devices": [{ "id": "h100-sxm-80gb", "...": "..." }] }

Values keep authored aliases, normalized decimal memory_gb, optional usable_memory_gb / host_memory_gb, and normalized partition capacities.

Optional successful-run evidence

HTTP/OpenAPI schema compatibility is a separate contract; see serving documentation. A capacity estimate and a completed generation do not establish HTTP client compatibility.

Add records inline under the manifest's capacity.reference_runs. One completed generation is useful; benchmarking every configuration is unnecessary. Follow the serving guide with the FastWan baseline and set the client's FASTVIDEO_MODEL=fastwan21-1.3b (or your actual served alias). FastWan requires FASTVIDEO_ATTENTION_BACKEND=VIDEO_SPARSE_ATTN; enable FASTVIDEO_STAGE_LOGGING=1 when collecting stage memory. A health check or model load alone is insufficient. Record the effective request, including inherited defaults.

Field Requirement
id, date Unique run ID within the model; date as YYYY-MM-DD
hardware, gpu_count Hardware catalog ID and actual device count
hardware_partition, hardware_allocation_gb Optional MIG/vGPU partition ID and provisioned allocation in decimal GB; a full-card run does not apply to a partition
outcome, execution completed; first_request or warm_request
evidence_url Accessible HTTP(S) logs or benchmark evidence
context.runtime, context.deployment Serving runtime ID and the cookbook deployment ID used for the run; the deployment must exist and use that runtime
context.revisions fastvideo and model; optional components mapping by role
context.config Complete effective serving configuration, inline; matching model and GPU count
context.request Normalized request with sampling.height, sampling.width, and sampling.num_frames as positive integers
context.env Complete launch environment snapshot mapped to strings; use {} when none was set
memory Optional measurements described below

Prefer immutable code/checkpoint revisions; pin separately replaced components. Preserve prompt, sampling, conditioning, runtime overrides, and relevant environment without credentials. An authored baseline is insufficient as an effective config snapshot. Validation checks structure and identity, not completeness of runtime capture.

Memory measurements

If present, memory requires a metric, matching scope, unit: GiB, and a per_gpu entry for each rank 0 through gpu_count - 1, without duplicates. torch_peak_allocated and torch_peak_reserved cover the PyTorch caching allocator (scope: allocator) and exclude the CUDA context and other processes. device_used covers whole-device usage (scope: device) and can support a fit comparison; allocator peaks cannot establish device fit by themselves. Every entry requires request_peak; startup_peak and named stages are optional. All peaks must be positive finite numbers; stage peaks use the same metric and cannot exceed request_peak.

This fragment uses hypothetical values to illustrate the format, not Wan measurements:

memory:
  metric: torch_peak_allocated
  scope: allocator
  unit: GiB
  per_gpu:
    - rank: 0
      request_peak: 8.4
      stages:
        encoding: 5.5
        denoising: 8.4
        decoding: 7.0

Stage logs report peak_allocated_mb and peak_reserved_mb in binary MiB; divide by 1024 for GiB. The human-readable reserved= is current reservation, not its peak. Allocated tensors, allocator reservation, and device-wide usage are different metrics; device-wide evidence must distinguish other processes.

The stage wrapper resets peak counters after each stage. A final counter alone cannot describe the entire request. Preserve stage peaks and verify complete-request coverage on every rank before using their maximum as request_peak. Establish startup/load boundaries separately so they do not contaminate request measurements. If logs cannot support a request peak, omit memory and keep successful-run evidence.

Successful-run evidence applies to its recorded model, hardware, and context; it does not verify every edited configuration. Calibration additionally requires matching metric, scope, phases, and execution context. One point can calibrate an existing scaling assumption, not establish accuracy across modes. Missing startup evidence leaves startup coverage unknown. Keep observations intact when deriving coefficients.

Static consumers

cookbook-capacity.mjs exposes these async loaders, also re-exported by the shared cookbook API/bundle:

Function Returns
loadCapacityIndex(url, options?) Index with model summaries, hardware_url, and resolved source url
loadCapacityModel(index, id, options?) Model with components mapped by role, examples, and reference_runs
loadCapacityHardware(index, options?) {version: 1, devices, url} with stable IDs and aliases

Start with the generated index URL, then pass the returned index to the other loaders. Each selected component is expanded with its ref and geometry; examples contain authored base_config and env. base_config includes hidden baseline settings, but is not a fully runtime-resolved configuration.

Each call accepts {fetcher, signal}; options are not retained between calls. Model/hardware paths resolve relative to the index's source URL (response.url, or the requested URL resolved against browser location). Use an absolute index URL outside a browser. Calls reject on fetch/HTTP/JSON failures, cancellation, invalid required structure/identity, or unknown model IDs. There is no module-level cache or retry.

These loaders fetch static facts; the calculator must resolve execution settings and check rule coverage before estimating memory or recommending hardware. assessCapacity() combines the analytical estimate with applicable measured evidence; matchEvidence() reports applicable, unverified, and rejected runs; recommendCapacity() returns measured or analytical fits and keeps unknown results explicit. No successful generation is treated as a minimum-VRAM measurement, and allocator-only peaks never establish device fit.