Date: 2026-07-15
Purpose: Survey existing implementations relevant to NXRT's scheduling/session management design.
vLLM implements request-level preemption with two modes for handling evicted sessions:
- SWAP mode (V0): Offloads KV cache blocks from GPU → CPU memory. When the request resumes, blocks are swapped back in without recomputation.
- RECOMPUTE mode (V1 default): Drops KV cache entirely; recomputes from prompt on resume. Lower overhead in V1's architecture.
# Scheduler decides preemption based on memory pressure
# PreemptionMode.SWAP or PreemptionMode.RECOMPUTE
# V0: configurable via --preemption-mode swap
# V1: recompute only (swap deprecated), but KV connectors (LMCache) proposed for offload- Scheduler is a central Python class (
vllm.core.scheduler) that maintains three queues: waiting, running, swapped - Preemption policy: oldest-first (FCFS priority by default)
- RFC #6077 proposes priority scheduling with forced preemption + KV cache preservation
| Aspect | Status |
|---|---|
| Preemption trigger | Hardcoded (memory threshold) |
| Preemption policy (which to evict) | Hardcoded FCFS, priority RFC pending |
| Swap vs Recompute | Configurable (V0), hardcoded recompute (V1) |
| Cost model | Latency-only (throughput maximization) |
| External cost signals (thermal, power) | Not supported |
- KV cache = session state analogy. vLLM's swap is essentially "session hibernation" for LLM inference state.
- Swap to CPU is expensive; V1 abandoned it for recompute. For NXRT: consider whether recompilation is cheaper than state serialization for small models.
- The scheduler is monolithic — no plugin interface for custom cost functions. This is a gap NXRT can fill.
- LMCache/KV connectors show the community wants modular offload backends.
TensorRT supports multiple optimization profiles per engine — each profile defines a (min, opt, max) shape range. At runtime, you can switch between profiles on an IExecutionContext to handle different input shapes optimally.
// Build time: define multiple profiles
IOptimizationProfile* profile1 = builder->createOptimizationProfile();
profile1->setDimensions("input", OptProfileSelector::kMIN, Dims4(1,3,224,224));
profile1->setDimensions("input", OptProfileSelector::kOPT, Dims4(8,3,224,224));
profile1->setDimensions("input", OptProfileSelector::kMAX, Dims4(32,3,224,224));
config->addOptimizationProfile(profile1);
IOptimizationProfile* profile2 = builder->createOptimizationProfile();
profile2->setDimensions("input", OptProfileSelector::kMIN, Dims4(32,3,224,224));
profile2->setDimensions("input", OptProfileSelector::kOPT, Dims4(64,3,224,224));
profile2->setDimensions("input", OptProfileSelector::kMAX, Dims4(128,3,224,224));
config->addOptimizationProfile(profile2);
// Runtime: switch profiles
context->setOptimizationProfileAsync(1, stream); // switch to profile 1- Profile switching triggers shape recomputation and tactic resource reallocation — not free, but much cheaper than recompilation
- Each profile independently selects optimal kernel tactics at build time for the
optshape - Single engine file contains all profiles; no separate compilation needed
- Cannot have two profiles with identical shape ranges (dedup)
| Aspect | Status |
|---|---|
| Profile definitions | User-defined at build time |
| Which profile to activate | User-controlled at runtime |
| Tactic selection within profile | Hardcoded (latency-optimal auto-tuning) |
| Switching cost model | Not exposed — user must measure |
| Thermal/power-aware profile selection | Not built-in (user must implement) |
- Pre-compiled multi-plan is proven. TensorRT validates the concept of building multiple execution strategies into one artifact.
- Profile switching has non-zero cost (shape propagation + resource recompute). NXRT should consider pre-computing transition costs.
- TensorRT doesn't help you decide when to switch — that's left to the application. NXRT's scheduler fills exactly this gap.
- The "opt shape" concept (performance sweet spot) maps to NXRT's notion of preferred operating points.
Core ML provides compute unit hints that control which hardware a model runs on:
MLComputeUnits.all— system decides (ANE + GPU + CPU)MLComputeUnits.cpuAndGPU— exclude ANEMLComputeUnits.cpuAndNeuralEngine— exclude GPUMLComputeUnits.cpuOnly
The runtime can split a model across units (e.g., some layers on ANE, others on GPU/CPU) based on op support.
- Core ML does NOT expose explicit thermal-aware scheduling APIs
- However, the system implicitly throttles: ANE draws ~12.7W vs GPU ~24.7W; GPU throttles 50%+ within 60s of sustained load while ANE stays stable
ProcessInfo.thermalState(iOS/macOS API) provides.nominal,.fair,.serious,.critical— apps can react by switching compute units- No built-in automatic switching based on thermals — must be app-implemented
let config = MLModelConfiguration()
config.computeUnits = .all // or .cpuAndNeuralEngine, etc.
let model = try MLModel(contentsOf: modelURL, configuration: config)
// Thermal monitoring (manual)
NotificationCenter.default.addObserver(
forName: ProcessInfo.thermalStateDidChangeNotification, ...) {
if ProcessInfo.processInfo.thermalState == .serious {
// Reload model with .cpuAndNeuralEngine (lower power)
}
}| Aspect | Status |
|---|---|
| Compute unit selection | User-controlled (load-time only) |
| Model splitting across units | System-controlled, not configurable |
| Thermal response | Manual (app must monitor + react) |
| Dynamic switching at runtime | Not supported without model reload |
| Cost model | Opaque (Apple internal) |
- No runtime dynamic switching — Core ML requires model reload to change compute units. NXRT can do better with pre-compiled multi-target plans.
- Thermal state is available but scheduling response is entirely app-responsibility. NXRT's scheduler should consume thermal signals automatically.
- The ANE vs GPU power/performance tradeoff is exactly the kind of multi-dimensional cost NXRT should model.
- Core ML's implicit model splitting shows value of heterogeneous execution — but lack of control is frustrating for developers.
QNN (successor to SNPE) provides a unified API with backend-specific libraries targeting:
- Snapdragon CPU (Kryo)
- Adreno GPU (OpenCL/Vulkan)
- Hexagon HTP/NPU (dedicated neural processor)
SNPE/QNN expose performance profiles:
SNPE_PERFORMANCE_PROFILE_DEFAULT
SNPE_PERFORMANCE_PROFILE_SUSTAINED_HIGH_PERFORMANCE
SNPE_PERFORMANCE_PROFILE_HIGH_PERFORMANCE
SNPE_PERFORMANCE_PROFILE_POWER_SAVER
SNPE_PERFORMANCE_PROFILE_SYSTEM_SETTINGS
SNPE_PERFORMANCE_PROFILE_BALANCED
These control DVFS (clock scaling) on the selected backend — not which backend to use.
// SNPE
snpe = snpeBuilder.setPerformanceProfile(
zdl::DlSystem::PerformanceProfile_t::HIGH_PERFORMANCE)
.setRuntimeOrder({zdl::DlSystem::Runtime_t::DSP,
zdl::DlSystem::Runtime_t::GPU_FLOAT16,
zdl::DlSystem::Runtime_t::CPU})
.build();
// QNN — backend selection at graph creation time
QnnGraph_Config_t graphConfig;
// Backend is selected by which backend library you load (libQnnHtp.so, libQnnGpu.so, etc.)| Aspect | Status |
|---|---|
| Backend selection | User-controlled (load/build time) |
| Performance profile | User-controlled (runtime changeable in SNPE) |
| Runtime backend switching | NOT supported without rebuild |
| Cost model for backend choice | Not provided — user benchmarks |
| Thermal response | System-level DVFS (transparent to app) |
| Custom scheduling policy | Not supported |
- Performance profiles are a good API pattern — simple enum that maps to system-level power/perf tuning.
- Like Core ML, backend choice is a build-time decision. No dynamic switching.
- The "runtime order" (fallback chain) in SNPE is interesting — NXRT could generalize this as a priority-ordered execution plan list.
- QNN's offline graph preparation (
snpe-dlc-graph-prepare) is analogous to NXRT's pre-compilation step.
TFLite's delegate architecture allows plugging in hardware-specific backends:
- XNNPACK (optimized CPU)
- GPU delegate (OpenGL ES / OpenCL / Metal / Vulkan)
- NNAPI delegate (Android neural networks API)
- Hexagon delegate (Qualcomm DSP)
- Core ML delegate (Apple)
// Android example
Interpreter.Options options = new Interpreter.Options();
GpuDelegate gpuDelegate = new GpuDelegate(new GpuDelegate.Options()
.setQuantizedModelsAllowed(true));
options.addDelegate(gpuDelegate);
Interpreter interpreter = new Interpreter(modelFile, options);- Delegates claim ops they support; unsupported ops stay on CPU
- Graph is partitioned at load time — delegate subgraphs + CPU fallback subgraphs
- No runtime switching between delegates without interpreter rebuild
| Aspect | Status |
|---|---|
| Delegate selection | User-controlled (build time) |
| Op partitioning | Automatic (delegate claims ops) |
| Runtime delegate switching | NOT supported |
| Custom delegates | Fully pluggable (implement C API) |
| Cost model / scheduling | None — user picks delegate |
| Thermal/power awareness | None at framework level |
- TFLite's delegate plugin API is the gold standard for extensible backend support — clean C interface, any vendor can implement.
- But scheduling is completely absent — you pick one delegate and that's it.
- Graph partitioning (split across backends) happens automatically but is load-time only.
- NXRT can learn from the plugin interface but must add the scheduling layer TFLite lacks.
- Hardware accelerator that dynamically selects kernels per operator based on actual dynamic dimension values
- Controller infers best-fit kernel for each op at runtime
- Relevance: Validates per-op adaptive execution plan selection, but at hardware level
- Comprehensive survey covering latency, cost efficiency, scalability
- Identifies open direction: ML-driven adaptive scheduling that considers multiple objectives
- Relevance: Confirms the gap — no existing system does multi-objective scheduling well
- Covers cluster-level scheduling (Gandiva, Tiresias, Pollux)
- Multi-resource scheduling (GPU memory + compute + network)
- Relevance: Shows multi-dimensional cost at cluster level; NXRT applies similar thinking at device level
- CPU-GPU thermal coupling modeling via system identification
- Proactive DTM (Dynamic Thermal Management) policies
- DVFS + task migration based on thermal predictions
- Relevance: Directly applicable — thermal-aware scheduling for heterogeneous compute. Key insight: scheduling must be proactive (predict thermal state) not just reactive.
- Theoretical analysis of optimal request scheduling
- Proves certain scheduling policies are optimal under specific assumptions
- Relevance: Foundation for proving NXRT scheduling properties
| Capability | vLLM | TensorRT | Core ML | QNN | TFLite | NXRT Goal |
|---|---|---|---|---|---|---|
| Session hibernation/swap | ✅ KV swap | ❌ | ❌ | ❌ | ❌ | ✅ Full state offload |
| Multiple pre-compiled plans | ❌ | ✅ Profiles | ❌ | ❌ | ❌ | ✅ Per-target plans |
| Runtime plan switching | ❌ | ✅ (with cost) | ❌ (reload) | ❌ (rebuild) | ❌ (rebuild) | ✅ Zero-copy switch |
| Pluggable cost model | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ User-defined |
| Multi-dimensional cost | ❌ | ❌ | ❌ | Partial (perf profiles) | ❌ | ✅ Latency+power+thermal+memory |
| Thermal-aware scheduling | ❌ | ❌ | Manual only | System DVFS | ❌ | ✅ Automatic |
| Backend plugin interface | ❌ | ❌ | ❌ | ❌ | ✅ Delegates | ✅ |
- No existing runtime combines pre-compiled multi-plan + automatic scheduling. TensorRT has multi-plan; none have automatic switching based on runtime conditions.
- No pluggable cost model exists. Every system uses hardcoded latency-only optimization or simple enum-based power modes.
- Session hibernation is LLM-specific (KV cache). No general-purpose compiled model session suspend/resume exists.
- Thermal-aware scheduling is always app-responsibility. No runtime does it automatically.
- NXRT's
CostModeltrait (pluggable, multi-dimensional) is genuinely novel in this space - Pre-compiling N plans + scheduler that picks based on runtime signals = unique combination
- Session state serialization (weights + activations + scratch buffers) for hibernation goes beyond what any current system offers for compiled models
- The closest analog to NXRT's full vision is a combination of: TensorRT profiles + vLLM swap + Core ML thermal monitoring + TFLite delegate extensibility