Skip to content

Strategies

Offloading strategies determine which model weights to move from GPU to CPU memory. Pass a strategy instance to OffloadConfig(load_strategy=...).

Strategy Protocol

Strategy

Bases: Protocol

Protocol for offload strategies.

All offload strategies must implement this protocol to be usable with the OffloadConfig and TensorManager.

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Compute offload strategy.

PARAMETER DESCRIPTION
layer_stats

Per-trap statistics containing tensor info and durations.

TYPE: list[LayerStatistics]

memory_stats

Optional dict mapping tensor size (bytes) to transfer time (ms).

TYPE: dict[int, float] | None DEFAULT: None

max_gpu_mem_bytes

Hard GPU memory limit in bytes. When set (memory mode), the strategy must respect this constraint. When None (latency mode), only scale governs offloading.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
StrategyResult

StrategyResult containing strategy_map and optional block_data.

Result Types

StrategyResult dataclass

StrategyResult(strategy_map: dict[str, list[TensorStatistics]], block_data: BlockStrategyData | None = None)

Result from strategy.compute().

All strategies return this structure for consistency. TensorManager uses the appropriate fields based on loader_type.

strategy_map instance-attribute

strategy_map: dict[str, list[TensorStatistics]]

Maps trap label to list of weights to offload.

block_data class-attribute instance-attribute

block_data: BlockStrategyData | None = None

Block allocation data. None if n_blocks=0 or not computed.

BlockStrategyData dataclass

BlockStrategyData(label_to_size_map: dict[str, int], allocation_ordered: dict[int, list[str]], block_sizes: dict[int, int] | list[int], label_to_block_id: dict[str, int], transfer_to_compute_map: dict[str, str])

Block allocation data computed by strategies.

This contains all data needed for block-based loaders to execute pipelined CPU→GPU transfers.

label_to_size_map instance-attribute

label_to_size_map: dict[str, int]

Maps trap label to total size of weights to offload.

allocation_ordered instance-attribute

allocation_ordered: dict[int, list[str]]

Maps block_id to ordered list of trap labels assigned to it.

block_sizes instance-attribute

block_sizes: dict[int, int] | list[int]

Size of each block in bytes (max trap size per block).

label_to_block_id instance-attribute

label_to_block_id: dict[str, int]

Maps trap label to assigned block index.

transfer_to_compute_map instance-attribute

transfer_to_compute_map: dict[str, str]

Maps transfer trap label to compute trap label for pipelining.

Knapsack Strategies

KnapsackStrategy

KnapsackStrategy(scale: float = 1.0, cyclic: bool = True, group_size: int = 1, threshold_mb: float = 0.1, n_blocks: int = 4)

Knapsack-based offloading strategy using duration-based optimization.

Initialize KnapsackStrategy for weight offloading.

PARAMETER DESCRIPTION
scale

Duration multiplier (baseline). 0.9 = 10% safety margin, 1.0 = exact fit, 2.0 = allow 100% overhead. When max_gpu_mem_bytes is set, scale may be increased automatically to meet the memory constraint.

TYPE: float DEFAULT: 1.0

cyclic

Whether to apply cyclic strategy extension.

TYPE: bool DEFAULT: True

group_size

Number of layers to merge into groups.

TYPE: int DEFAULT: 1

threshold_mb

Minimum tensor size threshold in MB.

TYPE: float DEFAULT: 0.1

n_blocks

Number of blocks for block-based loaders. Set to 0 to skip block data computation.

TYPE: int DEFAULT: 4

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Compute offload strategy using knapsack optimization.

PARAMETER DESCRIPTION
layer_stats

List of layer statistics containing tensor info and durations.

TYPE: list[LayerStatistics]

memory_stats

Optional dict mapping tensor size (bytes) to transfer time (ms). If not provided, extracts from layer stats.

TYPE: dict[int, float] | None DEFAULT: None

max_gpu_mem_bytes

Hard GPU memory limit in bytes (never exceed). When None, latency mode — respect scale strictly.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
StrategyResult

StrategyResult containing strategy_map and optional block_data.

KnapsackBlockStrategy

KnapsackBlockStrategy(scale: float = 1.0, group_size: int = 1, threshold_mb: float = 0.1, n_blocks: int = 4)

Block-based knapsack strategy that uses externally provided memory transfer statistics.

This strategy expects memory_stats to be provided by TensorManager, which handles the live GPU benchmarking. This separates concerns: TensorManager manages profiling, strategies focus on optimization logic.

Initialize KnapsackBlockStrategy for weight offloading.

PARAMETER DESCRIPTION
scale

Duration multiplier (baseline). 0.9 = 10% safety margin, 1.0 = exact fit, 2.0 = allow 100% overhead. When max_gpu_mem_bytes is set, scale may be increased automatically to meet the memory constraint.

TYPE: float DEFAULT: 1.0

group_size

Number of layers to merge into groups.

TYPE: int DEFAULT: 1

threshold_mb

Minimum tensor size threshold in MB.

TYPE: float DEFAULT: 0.1

n_blocks

Number of blocks for block-based loaders.

TYPE: int DEFAULT: 4

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Compute offload strategy using block-based knapsack optimization.

PARAMETER DESCRIPTION
layer_stats

List of layer statistics containing tensor info and durations.

TYPE: list[LayerStatistics]

memory_stats

Dict mapping tensor size (bytes) to transfer time (ms). Should be provided by TensorManager via live GPU benchmarking. If not provided, falls back to extracting from layer stats.

TYPE: dict[int, float] | None DEFAULT: None

max_gpu_mem_bytes

Hard GPU memory limit in bytes (never exceed). When None, latency mode — respect scale strictly.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
StrategyResult

StrategyResult containing strategy_map and block_data.

AdaptiveKnapsackStrategy

AdaptiveKnapsackStrategy(scale: float = 1.0, loader_type: str = 'strategy', cyclic: bool = True, group_size: int = 1, threshold_mb: float = 0.1)

Adaptive strategy that selects between KnapsackStrategy and KnapsackBlockStrategy based on the loader type configuration.

This provides a unified interface that automatically selects the appropriate strategy implementation based on how tensors will be loaded during inference.

Initialize AdaptiveKnapsackStrategy.

PARAMETER DESCRIPTION
scale

Duration multiplier (baseline). See KnapsackStrategy for details.

TYPE: float DEFAULT: 1.0

loader_type

Type of tensor loader to use. Options: - "strategy": Uses KnapsackStrategy (profiled tensor data) - "raw_block_transfer": Uses KnapsackBlockStrategy - "allocation_block_transfer": Uses KnapsackBlockStrategy

TYPE: str DEFAULT: 'strategy'

cyclic

Whether to apply cyclic strategy extension.

TYPE: bool DEFAULT: True

group_size

Number of layers to merge into groups.

TYPE: int DEFAULT: 1

threshold_mb

Minimum tensor size threshold in MB.

TYPE: float DEFAULT: 0.1

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Compute offload strategy using the appropriate strategy implementation.

PARAMETER DESCRIPTION
layer_stats

List of layer statistics containing tensor info and durations.

TYPE: list[LayerStatistics]

memory_stats

Optional dict mapping tensor size (bytes) to transfer time (ms).

TYPE: dict[int, float] | None DEFAULT: None

max_gpu_mem_bytes

Hard GPU memory limit in bytes (never exceed). When None, latency mode — respect scale strictly.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
StrategyResult

StrategyResult containing strategy_map and block_data.

Budget-First Strategy

BudgetFillStrategy

BudgetFillStrategy(n_blocks: int = 0, *, threshold_mb: float = 1.0, scale: float = 1.0, assignment_strategy: AssignmentStrategy | None = None, enable_layer_de: bool = True, enable_tensor_de: bool = True, pop_size: int = 20, epoch: int = 60, max_early_stop: int = 20, seed: int | None = None)

BudgetFill facade: greedy, then optional layer / tensor DE solvers.

Always runs :class:BudgetFillGreedyStrategy. Optionally runs :class:BudgetFillLayerDEStrategy (enable_layer_de, default True) and :class:BudgetFillTensorDEStrategy (enable_tensor_de, default True), keeping the best feasible plan with the fewest offloaded bytes. Pinching the budget does not prove min-offload, so DE is not skipped solely for a tight peak — Adaptive constructs BudgetFill with enable_tensor_de=False and uses :class:~flextensor.strategy.global_strategy.GlobalTensorSelectionStrategy under extra_optimization instead.

Each solver remains independently usable for A/B comparison.

PARAMETER DESCRIPTION
n_blocks

Pipeline block count (0 or >= 2; 1 rejected).

TYPE: int DEFAULT: 0

threshold_mb

Ignore tensors at or below this size (MiB).

TYPE: float DEFAULT: 1.0

scale

Multiplier on previous-layer duration for transfer-fit ranking.

TYPE: float DEFAULT: 1.0

assignment_strategy

Trap-to-block assignment (default Strict).

TYPE: AssignmentStrategy | None DEFAULT: None

enable_layer_de

Run per-layer DE after greedy.

TYPE: bool DEFAULT: True

enable_tensor_de

Run per-tensor DE after greedy/layer DE (default True; Adaptive keeps this False).

TYPE: bool DEFAULT: True

pop_size / epoch / max_early_stop

Caps forwarded to DE solvers.

seed

RNG seed for DE solvers.

TYPE: int | None DEFAULT: None

selected_solver_name property

selected_solver_name: str | None

Name of the solver that produced the last :meth:compute result.

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Run greedy, then optional scipy solvers; keep the best feasible plan.

BudgetFillGreedyStrategy

BudgetFillGreedyStrategy(n_blocks: int = 0, *, threshold_mb: float = 1.0, scale: float = 1.0, assignment_strategy: AssignmentStrategy | None = None)

Ranked-prefix BudgetFill: scan fit-then-size prefixes under a hard peak budget.

PARAMETER DESCRIPTION
n_blocks

Pipeline block count. 0 skips block planning (peak is residency only). 1 is rejected; use 0 or >= 2.

TYPE: int DEFAULT: 0

threshold_mb

Ignore tensors at or below this size (MiB).

TYPE: float DEFAULT: 1.0

scale

Multiplier on previous-layer duration for transfer-fit ranking.

TYPE: float DEFAULT: 1.0

assignment_strategy

Trap-to-block assignment. Defaults to :class:~flextensor.strategy.assignment.StrictRoundRobinAssignment. Pass :class:~flextensor.strategy.assignment.OptimizedRoundRobinAssignment to search block counts / patterns like GlobalOffload.

TYPE: AssignmentStrategy | None DEFAULT: None

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Select a minimal ranked offload prefix that meets max_gpu_mem_bytes.

BudgetFillLayerDEStrategy

BudgetFillLayerDEStrategy(n_blocks: int = 0, *, threshold_mb: float = 1.0, scale: float = 1.0, assignment_strategy: AssignmentStrategy | None = None, pop_size: int = 20, epoch: int = 60, max_early_stop: int = 20, seed: int | None = None)

Per-layer differential-evolution BudgetFill (one offload fraction per layer).

Same hard/soft goals as :class:BudgetFillGreedyStrategy, with search space ~n_layers. Within each layer, tensors are taken in fit-then-size order up to the chosen byte budget. Used by :class:BudgetFillStrategy when enable_layer_de=True, or alone for comparison.

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Optimize per-layer offload fractions under max_gpu_mem_bytes via DE.

BudgetFillTensorDEStrategy

BudgetFillTensorDEStrategy(n_blocks: int = 0, *, threshold_mb: float = 1.0, scale: float = 1.0, assignment_strategy: AssignmentStrategy | None = None, pop_size: int = 30, epoch: int = 80, max_early_stop: int = 25, seed: int | None = None)

Per-tensor differential-evolution BudgetFill (slow; opt-in fallback).

Same hard/soft goals as :class:BudgetFillGreedyStrategy, but searches arbitrary subsets via differential evolution (scipy.optimize.differential_evolution).

Needed when the best feasible set is not a within-layer fit→size prefix (e.g. take a mid-ranked tensor without its higher-ranked neighbors). Prefer :class:BudgetFillLayerDEStrategy for speed; enable via :class:BudgetFillStrategy enable_tensor_de=True or run alone. Adaptive leaves this off and uses :class:GlobalTensorSelectionStrategy for expensive tensor-level search under extra_optimization.

Seeds the population with the greedy ranked-prefix plan.

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Optimize a binary offload mask under max_gpu_mem_bytes via DE.

Simple Strategies

GreedyStrategy

GreedyStrategy(threshold_mb: float = 0.1, n_blocks: int = 4, scale: float = 1.0)

Greedy offloading strategy based on cumulative compute time heuristic.

Initialize GreedyStrategy.

PARAMETER DESCRIPTION
threshold_mb

Minimum tensor size threshold in MB.

TYPE: float DEFAULT: 0.1

n_blocks

Number of blocks for block-based loaders.

TYPE: int DEFAULT: 4

scale

Multiplier on the cumulative compute budget (< 1 adds safety margin, > 1 allows more transfers).

TYPE: float DEFAULT: 1.0

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Compute greedy offload strategy.

PARAMETER DESCRIPTION
layer_stats

List of layer statistics.

TYPE: list[LayerStatistics]

memory_stats

Unused, accepted for interface consistency.

TYPE: dict[int, float] | None DEFAULT: None

max_gpu_mem_bytes

Unused, accepted for interface consistency.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
StrategyResult

StrategyResult containing strategy_map and optional block_data.

NthLayerStrategy

NthLayerStrategy(nth_layer: int, threshold_mb: float = 0.1, n_blocks: int = 4)

Offloading strategy that offloads every Nth layer.

Initialize NthLayerStrategy.

PARAMETER DESCRIPTION
nth_layer

Interval between offloaded layers.

TYPE: int

threshold_mb

Minimum tensor size threshold in MB.

TYPE: float DEFAULT: 0.1

n_blocks

Number of blocks for block-based loaders.

TYPE: int DEFAULT: 4

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Compute Nth layer offload strategy.

PARAMETER DESCRIPTION
layer_stats

List of layer statistics.

TYPE: list[LayerStatistics]

memory_stats

Unused, accepted for interface consistency.

TYPE: dict[int, float] | None DEFAULT: None

max_gpu_mem_bytes

Unused, accepted for interface consistency.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
StrategyResult

StrategyResult containing strategy_map and optional block_data.

Global Strategies

GlobalOffloadStrategy

GlobalOffloadStrategy(n_blocks: int = 4, scale: float = 1.0, threshold_mb: float = 0.1, min_blocks: int | None = None, max_blocks: int | None = None, assignment_strategy: AssignmentStrategy | None = None)

Global offload strategy using assignment-based layer-to-block mapping for pipelined execution.

Pipeline Execution Model: - Layer N computes using Block X (data transferred during Layer N-1) - While computing Layer N, we transfer Block Y for Layer N+1 - Constraint: Block Y ≠ Block X (can't transfer and compute same block simultaneously) - Block X becomes free after Layer N finishes → can be reused for Layer N+2

This means: - Minimum 2 blocks required for pipelining - With 2 blocks: alternating pattern (A, B, A, B, ...) - With 3+ blocks: more flexibility for scheduling

The strategy uses an AssignmentStrategy (default: OptimizedRoundRobinAssignment) to determine layer-to-block assignments, then calculates block sizes based on the maximum weight size assigned to each block.

Initialize GlobalOffloadStrategy.

PARAMETER DESCRIPTION
n_blocks

Number of reusable memory blocks (minimum 2 for pipelining).

TYPE: int DEFAULT: 4

scale

Duration multiplier (baseline). Controls how much of the previous layer's compute time is available for transfers. 1.0 = exact fit.

TYPE: float DEFAULT: 1.0

threshold_mb

Minimum weight size threshold in MB. Weights smaller than this are kept on GPU and not offloaded.

TYPE: float DEFAULT: 0.1

min_blocks

Minimum number of blocks to use (default: 2). - 2 blocks: With alternating pattern (A,B,A,B,...)

TYPE: int | None DEFAULT: None

max_blocks

Maximum number of blocks to use (default: n_blocks).

TYPE: int | None DEFAULT: None

assignment_strategy

Strategy for layer-to-block assignment. Default: OptimizedRoundRobinAssignment (best GPU utilization). Available strategies: - OptimizedRoundRobinAssignment: Optimized cyclic pattern (recommended) - StrictRoundRobinAssignment: Strict i % n_blocks pattern

TYPE: AssignmentStrategy | None DEFAULT: None

RAISES DESCRIPTION
ValueError

If n_blocks < 2 (minimum required for pipelining).

ValueError

If min_blocks > max_blocks.

ValueError

If min_blocks < 2 or max_blocks > n_blocks.

ValueError

If scale <= 0.

calculate_layer_tensor_size staticmethod

calculate_layer_tensor_size(tensors: list[TensorStatistics]) -> int

Calculate total size of tensors in a layer.

calculate_peak_memory_pipelined staticmethod

calculate_peak_memory_pipelined(block_sizes: list[int], non_offloaded_memory: int) -> int

Calculate peak GPU memory for pipelined execution.

Peak memory includes: - All blocks (must be pre-allocated on GPU for pipelining) - Non-offloaded tensors (always stay on GPU)

PARAMETER DESCRIPTION
block_sizes

Size of each block in bytes (integers).

TYPE: list[int]

non_offloaded_memory

Memory used by tensors that are not offloaded.

TYPE: int

RETURNS DESCRIPTION
int

Total peak GPU memory in bytes (integer).

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Compute offload strategy using pipelined block optimization.

When max_gpu_mem_bytes is set (memory mode), the strategy will automatically increase scale beyond its initial value if the initial offloading is insufficient to meet the memory constraint.

PARAMETER DESCRIPTION
layer_stats

List of layer statistics containing tensor info and durations.

TYPE: list[LayerStatistics]

memory_stats

Optional dict mapping tensor size (bytes) to transfer time (ms). If provided, uses this data. Otherwise falls back to extraction from layer stats.

TYPE: dict[int, float] | None DEFAULT: None

max_gpu_mem_bytes

Hard GPU memory limit in bytes (never exceed). When None, latency mode — offload what fits in scale * duration.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
StrategyResult

StrategyResult containing strategy_map and block_data.

GlobalTensorSelectionStrategy

GlobalTensorSelectionStrategy(scale: float = 1.0, scale_ub: float | None = None, n_blocks: int = 4, min_blocks: int | None = None, max_blocks: int | None = None, threshold_mb: float = 0.1, pop_size: int = 50, epoch: int = 200, max_early_stop: int = 25, optimizer: str = 'DE', seed_ratio: float = 0.25, seed: int | None = None, assignment_strategy: AssignmentStrategy | None = None, transfer_window: TransferWindowCalculator | None = None)

Weight-level metaheuristic optimization strategy for GPU memory offloading.

This strategy optimizes the binary decision of whether to offload each weight, maximizing GPU memory utilization while respecting memory limits and ensuring transfers fit within pipelined execution windows.

Unlike GlobalOffloadStrategy which optimizes block sizes and layer assignments, this strategy performs fine-grained weight selection to find the optimal subset of weights to offload.

Decision variables: - Binary x[i] for each weight: 1 = offload, 0 = keep on GPU - Continuous scale: multiplier for compute time (transfer window)

Constraints: - Hard: Peak GPU memory <= max_gpu_mem_bytes (when set) - Hard: Transfer time for layer N <= (layer N-1's compute time) * scale

Objective (maximize): - GPU utilization relative to max_gpu_mem (when set) - Offload ratio (secondary) - Prefer lower scale (less latency overhead)

Memory Model: - Tensors NOT offloaded stay on GPU permanently (preloaded) - Peak GPU = sum(all kept tensors) + sum(block sizes) - Blocks are pre-allocated for pipelined transfers

Supported Optimizers (via scipy): - DE: Differential Evolution (scipy.optimize.differential_evolution) - RECOMMENDED default. Natively handles binary variables via integrality constraints. Best for high-dimensional problems with many tensors. - SA: Simulated Annealing (scipy.optimize.dual_annealing) - Treats variables as continuous; less efficient for binary decisions. May be useful for small problems with few tensors.

Initialize GlobalTensorSelectionStrategy.

PARAMETER DESCRIPTION
scale

Duration multiplier (baseline / lower bound for the optimizer). 0.9 = 10% safety margin, 1.0 = exact fit (default).

TYPE: float DEFAULT: 1.0

scale_ub

Upper bound for scale the optimizer may explore. When None (default), defaults to scale (latency mode). compute() automatically widens to 100.0 in memory mode (max_gpu_mem_bytes set) unless the caller explicitly set scale_ub. When explicitly set, used as-is. Example: scale=0.9, scale_ub=1.0 means transfers should fit within 90-100% of compute time.

TYPE: float | None DEFAULT: None

n_blocks

Number of memory blocks (max). Used if assignment_strategy is None.

TYPE: int DEFAULT: 4

min_blocks

Minimum number of blocks to try (default: 2).

TYPE: int | None DEFAULT: None

max_blocks

Maximum number of blocks to try (default: n_blocks).

TYPE: int | None DEFAULT: None

threshold_mb

Minimum tensor size threshold in MB (smaller tensors always kept on GPU).

TYPE: float DEFAULT: 0.1

pop_size

Population size for optimization.

TYPE: int DEFAULT: 50

epoch

Maximum number of optimization epochs.

TYPE: int DEFAULT: 200

max_early_stop

Stop if no improvement for this many epochs.

TYPE: int DEFAULT: 25

optimizer

Optimizer to use (default: "DE"). Available options: - "DE": Differential Evolution (scipy.optimize.differential_evolution). RECOMMENDED. Natively handles binary variables via integrality constraints. Best for high-dimensional problems with many tensors. - "SA": Simulated Annealing (scipy.optimize.dual_annealing). Treats variables as continuous; less efficient for binary decisions. May be useful for small problems with few tensors.

TYPE: str DEFAULT: 'DE'

seed_ratio

Ratio of population to seed with feasible solutions (0.0-1.0). Lower ratio = more random exploration.

TYPE: float DEFAULT: 0.25

seed

Random seed for reproducibility. If None, uses non-deterministic seed.

TYPE: int | None DEFAULT: None

assignment_strategy

Strategy for assigning layers to blocks. Default: OptimizedRoundRobinAssignment (tries min_blocks to max_blocks). Options: StrictRoundRobinAssignment, OptimizedRoundRobinAssignment.

TYPE: AssignmentStrategy | None DEFAULT: None

transfer_window

Calculator for per-layer transfer windows. Default: SingleLayerWindow (previous layer's duration only). Use GapAwareWindow to exploit empty layers for larger transfer budgets.

TYPE: TransferWindowCalculator | None DEFAULT: None

scale property writable

scale: float

Duration multiplier (lower bound). Alias for scale_lb.

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Run the core optimizer.

PARAMETER DESCRIPTION
layer_stats

List of layer statistics containing tensor info and durations.

TYPE: list[LayerStatistics]

memory_stats

Optional dict mapping tensor size (bytes) to transfer time (ms). If provided, uses this data. Otherwise falls back to extraction from layer stats.

TYPE: dict[int, float] | None DEFAULT: None

max_gpu_mem_bytes

Hard GPU memory limit in bytes (never exceed). When None, the memory constraint is disabled and the optimizer only respects the transfer time constraint (latency mode).

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
StrategyResult

StrategyResult containing strategy_map and block_data.

Optimizer parameter values

GlobalTensorSelectionStrategy accepts an optimizer parameter with two accepted values: "DE" (differential evolution via scipy.optimize.differential_evolution) and "SA" (dual annealing via scipy.optimize.dual_annealing). "DE" is recommended for most cases as it natively handles binary variables. "SA" treats variables as continuous and is less efficient for binary decision problems; it may be useful for small problems with few tensors.

Adaptive Strategy

AdaptiveStrategy

AdaptiveStrategy(scale: float = 1.0, loader_type: str = 'allocation_block_transfer', cyclic: bool = False, group_size: int = 1, threshold_mb: float = 0.1, min_blocks: int = 2, n_blocks: int = 4, extra_optimization: bool = False)

Run all available strategies and return the best result.

For block-based loaders (the common path), the following candidates are evaluated:

  • :class:KnapsackBlockStrategy
  • :class:GlobalOffloadStrategy (Optimized assignment)
  • :class:GlobalOffloadStrategy (Strict assignment)
  • :class:BudgetFillStrategy (only when max_gpu_mem_bytes is set; layer DE on, tensor DE off — use enable_tensor_de=True only when calling BudgetFill directly as a non-prefix fallback)

With extra_optimization=True (slower):

  • :class:GlobalTensorSelectionStrategy (Optimized assignment)
  • :class:GlobalTensorSelectionStrategy (Strict assignment)

For non-block loaders only :class:KnapsackStrategy is used.

After all candidates run, the one with the lowest overhead that also satisfies the memory constraint (if any) is selected. The winning strategy's name is available via :attr:selected_strategy_name.

Example

strategy = AdaptiveStrategy(scale=1.0, n_blocks=4) result = strategy.compute(layer_stats, memory_stats, max_gpu_mem_bytes=50 * 1024**3) print(strategy.selected_strategy_name) 'KnapsackBlock'

Initialize AdaptiveStrategy.

PARAMETER DESCRIPTION
scale

Duration multiplier (baseline). 0.9 = 10% safety margin, 1.0 = exact fit, 2.0 = allow 100% overhead.

TYPE: float DEFAULT: 1.0

loader_type

Type of tensor loader. Determines which strategy candidates are instantiated.

TYPE: str DEFAULT: 'allocation_block_transfer'

cyclic

Whether to apply cyclic strategy extension (KnapsackStrategy only).

TYPE: bool DEFAULT: False

group_size

Number of layers to merge into groups (Knapsack strategies).

TYPE: int DEFAULT: 1

threshold_mb

Minimum tensor size threshold in MB.

TYPE: float DEFAULT: 0.1

min_blocks

Minimum number of blocks for OptimizedRoundRobinAssignment search range. Does not affect Strict or Knapsack candidates.

TYPE: int DEFAULT: 2

n_blocks

Number of memory blocks.

TYPE: int DEFAULT: 4

extra_optimization

If True, include slower :class:GlobalTensorSelectionStrategy candidates. BudgetFill stays on the default path (tensor DE remains off; Adaptive uses TensorSelection for expensive tensor-level search). Default False.

TYPE: bool DEFAULT: False

selected_strategy_name property

selected_strategy_name: str

Name of the strategy that produced the best result.

RAISES DESCRIPTION
RuntimeError

If accessed before :meth:compute has been called.

all_scores property

all_scores: list[StrategyScore]

Scores from all candidates evaluated in the last :meth:compute call.

compute

compute(layer_stats: list[LayerStatistics], memory_stats: dict[int, float] | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyResult

Run all strategy candidates and return the best result.

PARAMETER DESCRIPTION
layer_stats

List of layer statistics containing tensor info and durations.

TYPE: list[LayerStatistics]

memory_stats

Optional dict mapping tensor size (bytes) to transfer time (ms).

TYPE: dict[int, float] | None DEFAULT: None

max_gpu_mem_bytes

Hard GPU memory limit in bytes (never exceed). When None, latency mode — respect scale strictly.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
StrategyResult

class:StrategyResult from the best-scoring candidate.

Assignment Strategies

AssignmentStrategy

Bases: Protocol

Protocol for trap-to-block assignment strategies.

compute

compute(layer_sizes: list[int], n_blocks: int) -> list[int]
Compute layer-to-block assignment.
PARAMETER DESCRIPTION
layer_sizes

Size of each layer in bytes.

TYPE: list[int]

n_blocks

Number of available blocks.

TYPE: int

RETURNS DESCRIPTION
list[int]

List of block indices, one per layer.

StrictRoundRobinAssignment

Strict round-robin assignment.

Assigns traps to blocks using strict pattern: trap_i -> block (i % n_blocks). Always uses all available blocks in a fixed cyclic pattern (0,1,2,3,0,1,2,3,...). Does not optimize for memory — all blocks sized to fit the largest trap.

compute

compute(layer_sizes: list[int], n_blocks: int) -> list[int]

Compute simple cyclic assignment.

OptimizedRoundRobinAssignment

OptimizedRoundRobinAssignment(min_blocks: int = 3, max_blocks: int = 4, distribution_weight: float = 0.5)

Optimized round-robin assignment that minimizes memory while satisfying constraints.

Features: - Tries different numbers of blocks (min_blocks to max_blocks) - Uses exhaustive search for small problems, greedy for larger ones - Balances memory efficiency with reuse distance (distribution_weight)

PARAMETER DESCRIPTION
min_blocks

Minimum number of blocks to use.

TYPE: int DEFAULT: 3

max_blocks

Maximum number of blocks to use.

TYPE: int DEFAULT: 4

distribution_weight

Weight for reuse distance vs memory (0.0-1.0). 0.0 = pure memory optimization, 1.0 = pure distribution optimization.

TYPE: float DEFAULT: 0.5

compute

compute(layer_sizes: list[int], n_blocks: int) -> list[int]

Compute optimized assignment within block count range.

Transfer Windows

TransferWindowCalculator

Bases: Protocol

Protocol for computing effective transfer windows per trap.

In the pipelined offload model, the transfer for trap i starts during an earlier trap's compute. How much earlier depends on the strategy:

  • SingleLayerWindow assumes transfer happens during layer i-1 only.
  • GapAwareWindow extends the window backward through consecutive layers that carry no transfer of their own.

compute_window

compute_window(layer_idx: int, layer_offload_sizes: ndarray, layer_durations: ndarray) -> float

Compute the effective transfer window for a given layer.

PARAMETER DESCRIPTION
layer_idx

Index of the layer whose transfer window to compute.

TYPE: int

layer_offload_sizes

Per-layer offload sizes in bytes. For pre-optimisation checks this reflects permanent (known) offload; during optimisation it reflects the candidate solution's offload pattern.

TYPE: ndarray

layer_durations

Per-layer compute durations in milliseconds.

TYPE: ndarray

RETURNS DESCRIPTION
float

Effective transfer duration in milliseconds.

SingleLayerWindow

Transfer window limited to the immediately preceding layer's compute duration.

This is the conservative (original) behaviour: the transfer for layer i must complete within layer i-1's compute time.

compute_window

compute_window(layer_idx: int, layer_offload_sizes: ndarray, layer_durations: ndarray) -> float

Return layer_durations[layer_idx - 1].

GapAwareWindow

Transfer window that exploits gap layers for a larger budget.

Sums backward from layer i-1 through consecutive layers whose next layer has no offloaded tensors (i.e. no competing transfer). This captures both permanent gaps (layers with no offloadable tensors at all) and dynamic gaps (layers the optimiser chose not to offload in the current candidate solution).

A layer j is considered "busy" (stops the backward scan) when layer_offload_sizes[j + 1] > 0 — meaning that during layer j's compute, the transfer stream is occupied moving layer j+1's data.

compute_window

compute_window(layer_idx: int, layer_offload_sizes: ndarray, layer_durations: ndarray) -> float

Return the sum of durations of consecutive free layers before layer_idx.

Evaluation Utilities

StrategyComputeError

Bases: Exception

Raised when a strategy cannot produce a valid result for the given inputs.

This covers recoverable, expected failures such as insufficient layers for pipelining or missing memory-transfer data. :class:AdaptiveStrategy catches this exception to skip a failing candidate and try the next one.

Programming errors (TypeError, AttributeError, etc.) should not be wrapped in this exception — they must propagate so that bugs surface during development.

StrategyScore dataclass

StrategyScore(strategy_name: str, peak_memory_bytes: int, estimated_overhead: float, consecutive_violations: int, is_valid: bool)

Evaluation metrics for a single strategy result.

ATTRIBUTE DESCRIPTION
strategy_name

Human-readable name of the strategy that produced the result.

TYPE: str

peak_memory_bytes

Estimated peak GPU memory in bytes (blocks + non-offloaded tensors).

TYPE: int

estimated_overhead

Fractional overhead from synchronous transfers. 0.0 means all transfers fit within compute windows (perfect pipelining). 0.05 means 5% extra time beyond the baseline compute duration.

TYPE: float

consecutive_violations

Number of adjacent layer pairs assigned to the same block.

TYPE: int

is_valid

True when there are no violations and memory is within the limit.

TYPE: bool

evaluate_strategy_result

evaluate_strategy_result(result: StrategyResult, layer_stats: list[LayerStatistics], strategy_name: str, interpolator: MemoryTransferInterpolator | None = None, max_gpu_mem_bytes: int | None = None) -> StrategyScore

Score a strategy result for comparison.

PARAMETER DESCRIPTION
result

The :class:StrategyResult to evaluate.

TYPE: StrategyResult

layer_stats

Layer statistics from profiling (used for durations and tensor sizes).

TYPE: list[LayerStatistics]

strategy_name

Display name for this strategy candidate.

TYPE: str

interpolator

Transfer-time interpolator. Required for overhead calculation. When None, overhead is estimated as 0.0 (cannot be computed).

TYPE: MemoryTransferInterpolator | None DEFAULT: None

max_gpu_mem_bytes

Hard GPU memory limit. When set, results exceeding this limit are marked invalid.

TYPE: int | None DEFAULT: None

RETURNS DESCRIPTION
A

class:StrategyScore summarising the result quality.

TYPE: StrategyScore

strategy_has_transfer_gaps

strategy_has_transfer_gaps(strategy_map: dict[str, list[TensorStatistics]], layer_stats: list[LayerStatistics]) -> bool

Check if a strategy has unused layer slots where transfer rearrangement would help.

In the pipelined model, each of the first N-1 layers can serve as a transfer slot (transferring the next layer's data during its compute). If some of those slots are unused (because the corresponding next layer has no offloaded tensors), rearrange_transfers can move other transfers into those gaps for better amortisation.

PARAMETER DESCRIPTION
strategy_map

Mapping of layer labels to tensors being transferred during that layer's compute.

TYPE: dict[str, list[TensorStatistics]]

layer_stats

Layer statistics for all layers in the model.

TYPE: list[LayerStatistics]

RETURNS DESCRIPTION
bool

True if there are free slots that rearrangement could exploit.