Ahead-of-Time Tuning Guide
Ahead-of-Time Tuning Guide
Ahead-of-time tuning is a mode where you explicitly control which modules to tune. This method provides precise control over the tuning process and is recommended for production environments.
Overview
Ahead-of-time tuning follows a four-step workflow:
- Inspect: Analyze your model or pipeline to identify tuneable modules
- Wrap: Wrap selected modules for tuning
- Tune: Execute the tuning process across different backends
- Persist: Save and load tuned models for later deployment
This approach offers several advantages:
- Control: Explicitly choose which modules to tune, pick strategies and backends, and mix different technologies
- Performance: Benchmark and select optimal configurations
- Speed: Save the tuned model to a deployable artifact to be loaded on the production environment
- Reproducibility: Deterministic tuning results
Quick Start
Here’s a complete example using Stable Diffusion:
Detailed Workflow
1. Inspection Phase
The inspect function analyzes your model or pipeline to identify PyTorch modules that can be tuned. For a detailed guide on inspection, see the AOT Inspect Guide.
2. Wrapping Phase
Given the list of modules from the previous step, you can wrap them for tuning. Under the hood, each torch.nn.Module is wrapped (imagine a proxy object) with AITune Module which intercepts all forward calls to get data, tune the module and serve the tuned version.
The following line shows how to wrap modules.
You can also specify tuning strategies during wrapping:
If you would like to have more control over picking the modules, you can manually wrap torch.nn.Module. When wrapping, you can specify a strategy for each module separately; i.e., you can combine different strategies backends into one model.
User-provided dynamic shapes
When recorded samples do not cover every shape needed in production, pass an explicit shape contract to
ait.Module. Each mapping key is a tensor’s forward parameter path, and each value describes the tensor’s full rank:
- Use an integer for a fixed dimension.
- Use
BatchDimfor the logical batch dimension. - Use
DynamicDimfor any other bounded dynamic dimension.
min and max are inclusive. opt selects the preferred compilation shape and defaults to max when omitted.
Configure dynamic_shapes when constructing ait.Module directly; ait.wrap does not accept per-module shape
definitions.
The integer 3 fixes the channel dimension. height and width are shared because they have the same name, even
though they are separate DynamicDim objects. Shared definitions must have the same type and bounds.
Forward input paths are shown in the input_spec sample-metadata table in tuning logs. Use the Semantic Path
column as the dynamic_shapes key. Top-level tensors use their forward parameter name, while tensors nested in
dictionaries, sequences, dataclasses, or supported custom objects include every key, index, or attribute:
Use a string such as "x" for a top-level path and a tuple such as ("options", "mask"), ("items", 0), or
("request", "image") for a nested path. Inputs omitted from the mapping keep using shapes inferred from recorded
samples.
Each dynamic shape definition must match the corresponding input tensor’s rank and fixed dimensions, and its ranges must include all dimension sizes present in the tuning samples. The explicit definitions then determine the ranges used by supported AOT backends.
See the runnable ResNet dynamic-shapes example, which
records (1, 3, 224, 224) and runs the tuned model at (2, 3, 256, 256).
3. Tuning Phase
The tune function executes the actual tuning:
Tuning Parameters
- func: The wrapped callable (model or pipeline)
- dataset: Dataset for tuning. It can be a list of samples,
torch.utils.data.Dataset,DataLoaderFactory,Tensoror sequence of tensors, dictionaries, strings - batch_sizes: List of batch sizes to tune against. If not specified, values [1, 2] will be used
- max_num_batches_per_batch_size: Maximum number of batches per batch size. If None, all batches will be used
- device: Device to use for tuning. Defaults to “cuda:0”
- dry_run: If True, performs a dry run without actual tuning
- disable_external_logging: Disable logging from external libraries
- clear_cache: Clear AITune cache before tuning
- ignore_failing_modules: If True, modules that fail tuning fall back to eager execution and tuning continues
Tuning time depends on the tuned modules’ size, used strategy, and number of backends. Modules are tuned one by one. If a strategy has many backends to pick from, it takes the one that fulfills specific strategy criteria. Each backend is validated against returning proper numeric results (check against NANs and infinity) and output shapes.
Note: If you specify a batch size that is not a power of 2, it will be used to gather samples but the actual search for maximum throughput will round it up to the nearest power of 2.
4. Persistence Phase
Once tuned, you can save your model for later use. This is crucial for production deployments to avoid re-tuning every time:
The tuned artifact will be saved in the checkpoints folder. The save function creates several files:
tuned_model.ait: The compressed checkpoint containing tuned and original weightstuned_model_sha256_sums.txt: SHA256 hashes for verification
To do inference, you can load the tuned model/pipeline:
Custom Inference Functions
For complex pipelines, you can provide a custom inference function:
Inspect and tune with the same workload wrapper
When scalar arguments change module execution or input shapes, put those options in a workload wrapper and use that
same wrapper for both inspect() and tune(). This is common for diffusion pipelines where you want to tune for
several image sizes, step counts, guidance scales, or sequence-length settings.
The important part is that inspection and tuning must execute the same workload. If you inspect through a wrapper
that uses multiple scalar arguments but later call ait.tune(pipe, input_data) directly, tuning records a
different execution path and may miss graph variants or shape ranges.
Note for FLUX and Stable Diffusion pipelines: image size, step count, guidance scale, and sequence-length settings are usually scalar keyword arguments on the pipeline call, not tensor samples in the dataset. Put every option you want to tune, such as
height,width,num_inference_steps,guidance_scale, ormax_sequence_length, inside the workload wrapper and pass that wrapper to bothait.inspect()andait.tune(). Callingait.tune(pipe, input_data)directly after inspecting through a wrapper will not exercise those same variants during tuning.
With config.strict_mode=True (the default), different non-tensor argument values can create separate graphs. Keep
scalar arguments fixed when you want one graph, or exercise each option in the wrapper when those variants should be
tuned.
Configuration Options
AITune has configuration for the tuning process, and each backend has its configuration.
Global Configuration
You can configure AITune globally:
Backend-Specific Configuration
Each backend has its own corresponding configuration:
See backend-specific documentation:
- TensorRT Backend
- Torch-Inductor JIT Backend
- TorchAO Backend
- Torch TensorRT AOT Backend
- Torch TensorRT JIT Backend
Dry Run Mode
You can run tuning in dry-run mode. It records samples of data, detects batch and dynamic axes, and detects graphs of execution but does not call the actual backend to tune. This allows debugging if everything is working as expected.
The dry-run mode can be turned on with the proper argument:
Example output from dry-run
Next Steps
- Learn about Ahead-of-time Inspect for detailed module analysis
- Learn about Just-in-Time (JIT) Tuning as an alternative approach
- Explore Backend Configuration
- Review Tune Strategies
- See Deployment Guide