Harli co-locates PEFT finetuning with LLM decode instances on the same GPU, exploiting decode's ~60% idle SM capacity. A unified CUDA VMM memory allocator, two-stage LR latency predictor, and GreenContext-based QoS scheduler yield 46% avg finetune throughput gain with zero SLO violation.
Disaggregated LLM serving dedicates separate GPUs to prefill and decode. Decode instances are memory-bandwidth-bound: average SM utilization is only ~40% despite ~85% DRAM bandwidth utilization (profiled on Ada6000 with LLaMA3-8B). Dynamic workloads cause batch sizes to fluctuate wildly — frequently dropping below 64, where the number of warps ($Num_{warp} = (B/16) \times (H/16)$, with $H = 4096$ for LLaMA3-8B) cannot fill 142 SMs — leaving the majority of compute dark.

Paper's Figure 4, verbatim (caption: "The DRAM bandwidth and SM utilization of the decode phase under different configurations.").
The utilization gap is remarkably stable across sequence lengths, confirming that decode underutilization is a structural property of the memory-bound autoregressive workload rather than a transient artifact of specific input shapes. For $bs \leq 64$ on Ada6000, maximum warp count is 1024 against a hardware capacity of 4544 — only 22.5% occupancy.
Three-component design:
核心技术壁垒: PEFT's fixed batch size makes co-location interference stable and linearizable. This single property enables a 5μs LR prediction instead of expensive offline profiling, and collapses the joint inference-finetune optimization into a univariate heuristic: push inference latency to the QoS boundary. Without this stability (e.g., with full finetuning or variable-batch co-tenants), the entire prediction and scheduling framework would break down.
| Metric | Value |
|---|---|
| Finetune throughput gain vs SeparateMode | 46.2% avg, up to 92.0% (Ada6000) |
| Finetune throughput gain vs StaticMode | 75.1% avg, up to 120.5% (Ada6000) |
| Solo-run prediction error | <2% avg, ≤6% max |
| Co-run prediction error | <5% avg |
| Runtime prediction overhead | 5 μs per invocation |
| QoS violations | 0 across all configurations |
| Harli-TP over single-GPU Harli | +10.2% avg |
| Memory fragmentation | <100 MB typical |

Paper's Figure 6, verbatim (caption: "System overview.").
The system intercepts both SGLang's inference path and LlamaFactory's finetuning path within a single process. The unified memory allocator sits beneath both, managing a shared CUDA VMM pool. The latency predictor feeds per-token estimates to the scheduler, which dynamically adjusts GreenContext SM partitions. The scheduler operates at decode-step granularity for inference and per-layer granularity for finetune.

Paper's Figure 7, verbatim (caption: "The unified memory allocator.").
The allocator's 2D pool maps physical memory blocks to both KV cache virtual addresses (for inference) and general-purpose virtual addresses (for finetune). When inference demand grows, the allocator reclaims finetune blocks by triggering a layer swap-out within one decode QoS window. A pre-reserved memory threshold — $Memory_{reserved} = (T/50) \times max_{bs} \times Mem_{kv}$ — ensures inference never stalls waiting for finetune to release memory.
| Symbol | Definition | Domain |
|---|---|---|
| $Util_{SM\text{-}k_i}$ | SM utilization of kernel $i$ | [0, 1] |
| $Util_{DRAM\text{-}k_i}$ | DRAM bandwidth utilization of kernel $i$ | [0, 1] |
| $R_{k_i}$ | Time fraction of kernel $i$: $T_{k_i} / T_{overall}$ | [0, 1], $\sum R_{k_i} = 1$ |
| $bs$ | Current decode batch size | $\mathbb{Z}^+$ |
| $seqlen$ | Output sequence length | $\mathbb{Z}^+$ |
| $b_0, c_0, k_0$ | Solo-run LR coefficients | $\mathbb{R}$ |
| $b_1, k_1$ | Co-location LR coefficients | $\mathbb{R}$ |
| $SM_{infer}, SM_{ft}$ | SM ratio for inference / finetune | [0, 1] |
| $Latency_{Decode\text{-}sm}$ | Solo decode latency at given $SM_{infer}$ | ms |
| $B$ | Total memory bandwidth | accesses/s |
| $f_{infer}, f_{ft}$ | Memory demand rate of each task | accesses/s |
| $r_{infer}$ | Effective inference rate under contention | accesses/s |
| $T$ | Swap-out time for one transformer layer | ms |
| $Mem_{kv}$ | Per-token KV cache memory | bytes |
Weighted utilization (Eq 1): $Util_{SM\text{-}decode} = \sum Util_{SM\text{-}k_i} \times R_{k_i}$. The overall SM utilization is a time-weighted average across all kernels, where each kernel's contribution is proportional to its share of total execution time.
Solo-run latency model (Eq 2): $Latency_{Decode} = bs \cdot b_0 + c_0 + bs \cdot k_0 \cdot seqlen$. Decomposes decode latency into per-batch overhead, constant overhead, and a term proportional to $bs \times seqlen$ (total KV cache attention volume). Linearity in both $bs$ and $seqlen$ reflects the memory-bound regime where latency scales with data movement.
Co-location latency model (Eq 3): $Latency_{colo} = (SM_{infer} \cdot b_1 + SM_{ft} \cdot k_1) \times Latency_{Decode\text{-}sm}$. Multiplies solo-run latency by a degradation factor that grows linearly with finetune SM allocation. The factor captures bandwidth contention: more finetune SMs → more memory traffic → slower decode.
Bandwidth contention (§5.2.2): $r_{infer} = B \cdot f_{infer} / (f_{infer} + f_{ft})$. Under proportional bandwidth sharing, each task's effective rate is its demand fraction of total bandwidth. Slowdown factor is $(f_{infer} + f_{ft}) / B$, so $Latency_{colo} = (f_{infer} + f_{ft}) / B \times Latency_{Decode\text{-}sm}$.
Memory reservation (§4.4): $Memory_{reserved} = (T / 50) \times max_{bs} \times Mem_{kv}$. Reserves enough KV cache slots to absorb new tokens arriving during one layer swap-out ($T$ ms), assuming 50 ms decode QoS target, scaled by maximum batch size and per-token KV size.

Paper's Figure 11, verbatim (caption: "Comparison between Harli and two baselines, SeparateMode and StaticMode, in improving throughput of finetuning tasks while maintaining QoS for inference requests. The caption of each subfigure, X-Y, indicates using model X for inference and model Y for finetuning.").
Harli achieves 46.2% average finetune throughput gain on Ada6000 across all four model pairs (LLaMA-LLaMA, LLaMA-Qwen, Qwen-LLaMA, Qwen-Qwen). Ada6000 outperforms A100 for two structural reasons: more SMs (142 vs 108) create more spatial sharing headroom, and more memory (48GB vs 40GB) reduces swap frequency. StaticMode with fixed 60/40 partitioning consistently underperforms because it cannot adapt to load fluctuations — on A100, it even loses to SeparateMode because the fixed memory allocation starves finetune.

Paper's Figure 12, verbatim (caption: "Box plot of prediction error rates. Labels on the x-axis indicates the inference decoding latency prediction stage and models.").
Solo-run prediction (Stage 1) achieves <2% average error with ≤6% worst case. Co-run prediction (Stage 2) maintains <5% average error across all model pairs. The tight variance in the box plots confirms that PEFT's batch-size stability translates directly into prediction stability — the core assumption enabling the lightweight LR approach.

Paper's Figure 13, verbatim (caption: "The memory usage of both inference and finetune tasks, and the window size of the finetune task. Loads a, b, c denote light load, heavy load, and medium load respectively.").
Under the controlled trace (light load bs=8 → heavy load bs=42 → medium load bs=24), the unified allocator dynamically adjusts finetune's layer window inversely with inference memory demand. When inference load spikes, the window shrinks to accommodate more KV cache. When load drops, finetune expands its window, reducing swap overhead. The small-tensor pool remains constant throughout, confirming correct sizing at initialization.
| Step | Claim | Evidence | Dep |
|---|---|---|---|
| 1 | Decode instances in disaggregated serving are structurally underutilized: ~40% SM vs ~85% DRAM bandwidth | Profiling on Ada6000/A100 with splitwise trace (Fig 3-4); warp capacity analysis: $B < 64$ yields $< 1024$ warps against 4544 SM capacity | — |
| 2 | PEFT finetuning is an ideal co-location candidate: compute-bound, <0.3% parameter overhead, fixed batch size | Memory analysis (§2.1); simplified co-location experiment yields up to 101.2% finetune throughput gain without QoS loss (Fig 5) | 1 |
| 3 | Fixed batch size makes interference linearizable via LR with <5% error, bypassing expensive offline profiling | Two-stage LR model (§5.1-§5.2.1); theoretical validation via proportional bandwidth sharing model (§5.2.2); error distribution (Fig 12) | 2 |
| 4 | CUDA VMM enables inter-task memory sharing without disrupting KV cache allocation patterns | Unified allocator with 2D block pool + virtual address remapping (§4.2); window-based swapping + pre-reserved threshold for non-blocking coordination (§4.3-§4.4); dynamic behavior under load (Fig 13) | 1 |
| 5 | Pushing inference latency to QoS boundary implicitly maximizes finetune throughput without explicit finetune modeling | Empirical observation: finetune peaks when inference approaches QoS target (§5.2.3); bandwidth is the binding constraint, so minimizing inference's bandwidth headroom maximizes finetune's share | 3 |
| 6 | End-to-end system achieves 46.2% avg finetune throughput gain with zero QoS violations across all tested configurations | 1-hour evaluation with 19k+ requests on production trace, 4 model pairs, 2 GPU types (Fig 11); latency CDF stays under 40ms target | 3, 4, 5 |
[实现未公开] — Harli prototype built on SGLang (serving) and LlamaFactory (finetuning). ~4,000 LoC total: 2,500 C++ for unified memory allocator, 200 C++ for GreenContext-PyTorch integration, 1,300 Python for predictor and scheduler. No public source repository is referenced in the paper.
The hardest-to-replicate component is the unified memory allocator's CUDA VMM integration (2,500 of 4,000 total LoC). It requires replacing PyTorch's default memory allocator with a custom one that manages both KV cache slots and general-purpose tensors through virtual address remapping, implementing the 2D block pool with chunk-level allocation that preserves KV cache's zero-overhead index-based access while supporting arbitrary tensor shapes, and building the two-level allocation strategy (2MB chunks + 2KB buddy pool) to handle 5k+ small tensor allocations per finetune iteration without fragmentation. The predictor and scheduler are comparatively straightforward (1,300 Python lines for both), leveraging the stability that PEFT's fixed batch size provides.
loss.backward() triggers the entire backward graph in C++ and cannot be interrupted from Python. Harli works around this by splitting the model into per-layer submodels and manually chaining gradient computation through explicit tensor passing, creating per-layer scheduling points. Combined with micro-batching to keep each scheduling unit under ~10ms, this ensures finetune never blocks inference scheduling for more than one decode QoS window.