How auto_regressive_inference Manages Memory in Kronos for Long Predictions

The auto_regressive_inference function in Kronos bounds memory usage during long time-series predictions by using fixed-size pre and post token buffers updated via sliding-window torch.roll operations, maintaining constant memory complexity of O(max_context) regardless of the prediction length.

The Kronos open-source repository implements a transformer-based architecture for time-series forecasting. Unlike standard autoregressive decoders that cache the entire growing sequence, the auto_regressive_inference method defined in model/kronos.py restricts the attention window to a configurable max_context parameter. This design explicitly caps GPU memory consumption, enabling generation of arbitrarily long prediction horizons without proportional resource increases.

Input Preparation and Tokenization

Before entering the generation loop, the function prepares input tensors and converts continuous values into discrete token streams. This stage establishes the static buffer dimensions that govern memory throughout inference.

Value Clipping and Sample Replication

Raw input tensors are first clipped to control extreme values, then expanded to support parallel stochastic sampling. This operation determines the batch size that the sliding-window buffers must accommodate.

x = torch.clip(x, -clip, clip)
x = x.unsqueeze(1).repeat(1, sample_count, 1, 1).reshape(-1, x.size(1), x.size(2))

As implemented in model/kronos.py at lines 90-94, this reshaping creates a batch dimension that replicates the input across sample_count Monte Carlo trajectories. Crucially, this sample replication occurs before buffer allocation, ensuring that memory for parallel predictions is accounted for in the fixed-size buffers.

Dual-Stream Token Encoding

Kronos tokenizes continuous time-series values into two separate discrete streams, designated pre and post, which are handled independently during the autoregressive steps.

x_token = tokenizer.encode(x, half=True)

Lines 98-99 of model/kronos.py show this encoding step. The resulting x_token tuple contains the initial token indices that seed the fixed-size buffers, providing the historical context required for the first prediction step.

Sliding-Window Buffer Architecture

The core memory management strategy relies on two fixed-length tensors—pre_buffer and post_buffer—that act as a circular window into the most recent tokens. This architecture guarantees that memory usage never exceeds 2 × max_context × batch_size elements, regardless of how many autoregressive steps are executed.

Buffer Allocation and Initialization

The buffers are allocated as zero-initialized tensors with shape [batch_size, max_context] and immediately seeded with the trailing tokens from the input sequence.

pre_buffer = x_token[0].new_zeros(batch_size, max_context)
post_buffer = x_token[1].new_zeros(batch_size, max_context)

According to model/kronos.py lines 108-114, only the last max_context tokens from the encoded input are copied into these buffers. Older history falling outside this window is explicitly excluded from memory before generation begins, establishing the constant-size constraint.

Rolling Updates During Generation

Inside the inference loop, which iterates pred_len times, the buffers update in-place using conditional rolling logic. When the cumulative sequence length exceeds max_context, the buffers shift left to discard the oldest token and append the newly generated token at the final position.

if current_seq_len < max_context:
    pre_buffer[:, current_seq_len] = sample_pre.squeeze(-1)
    post_buffer[:, current_seq_len] = sample_post.squeeze(-1)
else:
    pre_buffer.copy_(torch.roll(pre_buffer, shifts=-1, dims=1))
    post_buffer.copy_(torch.roll(post_buffer, shifts=-1, dims=1))
    pre_buffer[:, -1] = sample_pre.squeeze(-1)
    post_buffer[:, -1] = sample_post.squeeze(-1)

This logic at lines 124-134 of model/kronos.py executes torch.roll to perform an in-place circular shift along the time dimension. By using copy_ rather than assignment, the operation avoids allocating new tensors, ensuring that peak GPU memory remains bounded even when generating thousands of future steps.

Reconstruction and Final Output

After the autoregressive loop completes, the full token sequence is assembled by concatenating the original input tokens with the generated tokens. The decoder then reconstructs continuous values using only the relevant context window.

full_pre = torch.cat([x_token[0], generated_pre], dim=1)
full_post = torch.cat([x_token[1], generated_post], dim=1)

Lines 146-163 of model/kronos.py demonstrate this reconstruction phase. The concatenated tensors are sliced to retain only the tokens necessary for decoding, and final predictions are produced by averaging across the sample_count dimension to yield the time-series forecast.

Memory Complexity and Performance Guarantees

Standard transformer decoders exhibit O(n²) memory complexity with sequence length due to caching key-value pairs for all previous positions. In contrast, the auto_regressive_inference implementation achieves O(max_context) constant memory usage. Because the rolling buffer strategy discards tokens outside the attention window, the GPU memory footprint scales only with the user-defined max_context parameter, not the prediction horizon. This makes it feasible to generate very long forecasts on hardware with limited VRAM.

End-to-End Usage Example

The following example demonstrates invoking the memory-efficient predictor for a 200-step forecast using a context window of only 256 tokens. Despite requesting more steps than the context size, memory usage remains bounded by the buffer limits.

import pandas as pd
from model.kronos import KronosPredictor
from some_model_loader import load_model, load_tokenizer

# Load pretrained components

model = load_model('kronos.pt')
tokenizer = load_tokenizer('kronos_tokenizer.pt')

# Initialize with max_context=256

predictor = KronosPredictor(model, tokenizer, max_context=256, clip=5)

# Prepare historical data

historical_df = pd.read_csv('data.csv', parse_dates=['timestamp'])
x_ts = historical_df['timestamp']
y_ts = pd.date_range(start=x_ts.iloc[-1] + pd.Timedelta(minutes=1),
                     periods=200, freq='1min')

# Generate long forecast with constant memory

forecast = predictor.predict(
    df=historical_df,
    x_timestamp=x_ts,
    y_timestamp=y_ts,
    pred_len=200,
    T=0.9,
    top_k=0,
    top_p=0.95,
    sample_count=4
)

In this scenario, auto_regressive_inference maintains allocation for exactly 256 token slots per buffer per sample, regardless of the 200-step generation length.

Summary

  • max_context bounding: Memory usage is explicitly capped at 2 × max_context × batch_size token slots by fixed-size pre_buffer and post_buffer tensors.
  • Sliding-window attention: The decoder attends only to the most recent tokens, with torch.roll operations shifting the window forward in-place.
  • In-place operations: Rolling buffer updates use copy_ and torch.roll to prevent temporary tensor allocations during the autoregressive loop.
  • Constant complexity: GPU memory consumption remains constant irrespective of pred_len, enabling long-horizon forecasting on limited hardware.
  • Dual-stream processing: Separate buffers for pre and post token streams ensure the discretized representation maintains bounded memory throughout generation.

Frequently Asked Questions

What is the memory complexity of auto_regressive_inference in Kronos?

The memory complexity is O(max_context), meaning it scales linearly with the configured context window size but remains constant with respect to the prediction length. The implementation explicitly allocates buffers of shape [batch_size, max_context] and never expands them during the generation loop.

How does the rolling buffer mechanism work during generation?

When the generated sequence length exceeds max_context, the function executes torch.roll(buffer, shifts=-1, dims=1) to shift all tokens left by one position, effectively discarding the oldest token. The newly generated token is then written to the final index. This logic, found at lines 124-134 of model/kronos.py, ensures the buffer size remains fixed.

Can prediction horizons exceed max_context?

Yes. The pred_len parameter can be arbitrarily large because auto_regressive_inference processes the sequence as a stream. At each step, the model attends only to the most recent max_context tokens stored in the rolling buffers, allowing for unlimited generation length with constant memory.

Where is the auto_regressive_inference function implemented?

The function is implemented in model/kronos.py within the shiyu-coder/Kronos repository, specifically between lines 89 and 170. This module contains the sliding-window logic, buffer initialization, and token management code referenced throughout this analysis.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →