GO TO A SECTION

↑ ↓ to navigate · Enter to openExplore all sections →
FDEInterviews logo
MLSys 2024

S-LoRA: Serving Thousands of Concurrent LoRA Adapters

One base model, thousands of customer fine-tunes: keep LoRA adapters in host memory, page them into the KV cache's GPU pool, and batch across adapters. 2,000 adapters on one GPU at 95% throughput.

Breakdown by FDE Interviews · 12 min read · Updated

About the original · 2024
AUTHORS
Sheng, Cao, Li, Hooper, et al.
FROM
UC Berkeley, Stanford University, Shanghai Jiao Tong University
PUBLISHED
Nov 2023 · MLSys 2024
CITED BY
239Semantic Scholar · Oct 2026
IN THIS BREAKDOWN · 10 SECTIONS

You can read 20% of this breakdown.

01

The paper in one minute

LoRA fine-tunes a model by learning two small matrices per weight, A and B, so the update to W is the product AB with a low rank r. A company that fine-tunes one base model for each customer or task ends up with hundreds or thousands of adapters. The LoRA paper's advice for serving was to merge AB into W, which removes the extra compute, and to add and subtract adapters to switch between them. With many adapters live at once, merging means either a full model copy per adapter or a GPU that serves one adapter at a time.

S-LoRA keeps W shared and computes x·A·B per request alongside the batched x·W. Since x·W dominates the cost and can be batched across all requests regardless of adapter, the small extra work pays for itself. Three pieces make it scale:

Host memory for adapters. All adapters live in CPU memory; only the ones used by the running batch are copied to the GPU, predicted from the waiting queue and prefetched during the current step.

Unified paging. Adapter weights (rank × hidden size) and KV cache (tokens × hidden size) share a dimension, so one memory pool with pages of one hidden-size vector stores both, extending vLLM's PagedAttention. Adapters of different ranks and sequences of different lengths come and go without fragmenting memory.

Kernels and tensor parallelism. Custom kernels (MBGMM for prefill in Triton, MBGMV for decode adapted from Punica) gather each request's adapter from non-contiguous pages and handle mixed ranks without padding. A tensor-parallel partition aligned with Megatron-LM's adds LoRA communication of 5(N-1)Br/N to the base model's 2(N-1)Bh/N, small because r is much less than h.

On an A100 80 GB with Llama-7B adapters, S-LoRA served 2,000 adapters at 95% of its 5-adapter throughput, about 4x vLLM running merged copies and about 30x Hugging Face PEFT.

THE IDEA, VISUALLY
WHERE THE ADAPTERS LIVE (SECTIONS 4-5) host memory: all adapters … fetch active adapters, prefetched GPU: base weights + one paged pool KV adapter free 1 page = H values ONE BATCH, MANY ADAPTERS (FIGURE 1) x · W for every request one batched GEMM + x · Aᵢ · Bᵢ per request custom kernels, mixed ranks Merging an adapter into W saves that compute, but every adapter then needs its own weight copy and batches can't mix adapters. Prefill kernel: MBGMM (Triton). Decode: MBGMV (from Punica). Tensor parallel: LoRA adds 5(N-1)Br/N of traffic to the base model's 2(N-1)Bh/N, about 2-4% at rank 64. Built on LightLLM; code at github.com/S-LoRA/S-LoRA THROUGHPUT, LLAMA-7B RANK 8, A100 80GB (TABLE 3) requests per second 5 adapters S-LoRA 8.05 vLLM, merged copies 2.04 HF PEFT 0.88 100 adapters S-LoRA 7.99 vLLM, merged copies out of memory HF PEFT 0.25 1,000 adapters S-LoRA 7.64 2,000 adapters S-LoRA 7.61 vLLM-packed runs one merged model per adapter; PEFT swaps adapters between batches with no paged KV cache THE TAKEAWAY ~4x vLLM-packed (5 adapters), 30-33x PEFT (100); 2,000 adapters keep 95% of the 5-adapter throughput (S1), limited by host memory, not GPU memory
S-LoRA treats adapter weights like KV cache: pages in one GPU pool, fetched from host memory only for the requests currently running, so one base model can serve thousands of fine-tunes in the same batch.
Research notes: the problem, approach and results
2,000

LoRA adapters served from one A100 80 GB at 95% of the throughput with 5 adapters (Llama-7B, rank 8)

~4x

throughput over vLLM serving one merged model per adapter, which ran out of memory by 100 adapters

~2-4%

extra tensor-parallel communication for rank-64 adapters relative to the base model, by the paper's cost formula

THE PROBLEM

Teams fine-tune one base model into many LoRA adapters, one per customer or task. Merging each adapter into the weights means a full model copy per adapter and no batching across them; swapping adapters between batches leaves the GPU idle. Neither scales past a handful of adapters.

THE IDEA

Keep the base weights shared and compute each request's low-rank term on the fly. Store all adapters in host memory, fetch only those the running batch needs, and manage adapter weights and KV cache in one paged pool to avoid fragmentation. Custom kernels batch requests with different adapters and ranks; a tensor-parallel layout keeps LoRA's extra communication small.

THE RESULT

On one A100 80 GB with Llama-7B adapters, S-LoRA served 2,000 adapters at 7.61 requests/s (8.05 with 5), against 2.04 for vLLM running merged copies (out of memory at 100) and 0.25 for Hugging Face PEFT at 100 adapters: up to 4x and 30x higher throughput.

02

Why it matters to an FDE

Per-customer or per-task fine-tuning is a common ask: a model that writes in each client's style, follows each team's schema, or knows each product line. The cost question is not training, which LoRA makes cheap, but serving. If every adapter needs its own GPU or its own model copy, the economics fail at a few dozen customers.

Multi-adapter serving changes the pricing. With adapters batched on a shared base model, an extra customer adds an adapter in host memory (tens of MB), not a GPU. That makes a per-customer model a product option rather than a bespoke deployment.

Pick a serving stack that batches across adapters. "LoRA support" in a server can mean merging one adapter at a time. The question to ask is whether requests for different adapters run in the same batch, and how adapter memory shares the GPU with the KV cache.

Popularity skew helps. Throughput depends on how many distinct adapters are in each batch, not how many exist. A long tail of rarely used adapters costs host memory and occasional fetches, not GPU time.

THE FULL BREAKDOWN

Understand the mechanism. Try it on a real problem.

Unlock this breakdown and 149 more papers.

Premium opens every paper in full, plus every answer, concept, course and guide on the site. One payment for six months of access.

6 months · One payment · No auto-renewal

Get Premium · ₹2,000 / $25
  • ◇1 architecture diagram
  • </>1 runnable code experiment
  • ▦2 results tables
  • ?5 interview questions with answers
  1. 03Key takeaways

    Merging vs on-the-fly, memory, scaling with adapter count, tensor parallelism and three misreadings

    Free account
  2. 04How it works

    Request flow, the memory pool, the kernels, tensor parallelism and admission control

    Premium
  3. 05The numbers that matter

    Setups, Table 3, scaling with adapter count, real traces and tensor parallelism

    Premium
  4. 06Try it yourself

    Recheck Table 3, size adapters in pages, test a unified pool, and model merging vs batching

    Premium
  5. 07Where it breaks

    Active-set growth, LoRA-only scope, older models, overhead, and slips

    Premium
  6. 08Using it on a deployment

    Sizing adapter memory, measuring the active set, and deciding when to merge instead

    Premium
  7. 09Interview angles

    Five questions this paper prepares you for, with answer hints

    Premium
Back to the paper library →