Read Once, Answer Many

Parallel Prompting for Shared-Context LLM Inference

Zekun ZhaoJeffrey Flanigan
University of California, Santa Cruz
Paper Code BibTeX
Prompt

Saturn's inside is similar to Jupiter's inside. At the centre is a small rocky core that is about the size of the Earth. Saturn's core is very hot.

Q1: Saturn's inside is similar to what planet?

Q2: What is at the center of Saturn?

Q3: What is the temperature of Saturn's core?

Parallel Prompting
the document and all questions go into one sequence
DocQ1Q2Q3
Prefilled once. Each question gets its own answer stream, and each stream sees only the document and its own question.
Generation timestep t = 0
Batch prompting answers one after another
DocQ1Q2Q3
done at t = 9
Parallel Prompting all answers at once
DocQ1Q2Q3
done at t = 4

Batch prompting also reads the document once, but it writes one answer after another, and later answers see the earlier ones. Parallel Prompting adds one token to every answer at each step and keeps the answers independent.

Inside decode step T1
Batched requests one request per question (stock SGLang)
3 vector × matrix products
Document keys read this step: 0 times
Parallel Prompting one request, three streams
DocQ1Q2Q3
×
Key tokenKey tokenKey tokenKey token
1 matrix × matrix product
Document keys read this step: 0 time
An illustration with the example from the paper. Parallel Prompting packs the questions after the document in one sequence and decodes all answers together: it takes fewer steps than batch prompting, and each step reads the document's keys once instead of once per question as batched requests do. Measured speedups follow below.

Speedup

Speedup of Parallel Prompting over stock SGLang, with CodeLlama-7B on 8k–16k-token documents and 64–256 questions each. Every run is paired: the same documents and questions are timed under each configuration on the same GPU.

3.7×faster than stock SGLang
(FlashInfer) on an A100
5.9–6.0×faster on an
RTX A6000
144 / 144paired runs won across
2 GPUs and 2 SGLang releases
vs. stock SGLang, FlashInfer + CUDA graphs vs. stock SGLang, Triton + CUDA graphs

Geometric mean of 36 paired runs per setting; whiskers are 95% bootstrap confidence intervals. The dashed line is no speedup.

Show as a table
SGLang · GPUvs. FlashInfervs. Triton

For example, Parallel Prompting answers 256 questions about a 16k-token document in 12.3 s, where stock SGLang needs 60.0 s (SGLang v0.4.2, A100).

Run the code

Parallel Prompting is an overlay on stock SGLang v0.5.22 and v0.4.2. Start from the official SGLang Docker image and copy it on:

git clone https://github.com/jlab-nlp/parallel-prompting.git
cd parallel-prompting
docker run --gpus all -it --shm-size 8g -v $PWD:/work -w /work \
    lmsysorg/sglang:v0.5.22 bash
bash install.sh 0.5.22   # inside the container

Then pass one document and its questions to the engine; each question gets its own stream:

from sglang.srt.server_args import ServerArgs
from sglang.srt.parallel_prompt.engine import ParallelPromptEngine

args = ServerArgs(
    model_path="codellama/CodeLlama-7b-Instruct-hf",
    attention_backend="triton", sampling_backend="pytorch",
    disable_cuda_graph=True, disable_radix_cache=True)
engine = ParallelPromptEngine(args, cuda_graph=True)
tok = engine.tokenizer

doc = tok.encode("Read the passage and answer briefly.\n\nPassage:\n...\n\n")
qs = [tok.encode(q, add_special_tokens=False)
      for q in ["Question: Who ...?\nAnswer:",
                "Question: When ...?\nAnswer:"]]
out = engine.generate([doc], [qs], max_new_tokens=64, parallel_size=2,
                      stop_token_ids=engine.default_stop_ids())
print([tok.decode(a) for a in out[0]])  # one answer per question

To compare against stock SGLang on your own GPU:

cd sglang-0.5.22
python3 pp_bench/demo_gen.py --doc-tokens 12000 --num-questions 256 \
    --answer-tokens 32 --parallel-size 32 --cuda-graph --compare

The README has the commands behind every table and figure in the paper.

Abstract

LLM workloads sometimes ask many independent questions about the same long document, as in document QA, data labeling and LLM-based evaluation. Prefix caching removes the redundant prefill shared by these queries, but each decode step still reads the document's key–value (KV) cache once per question, so decoding is bound by memory bandwidth and scales with the number of questions.

We present Parallel Prompting, which decodes P questions as P independent streams of a single request. Every step reads the shared document once for all P streams, and a stream-id attention mask, evaluated inside the attention kernel, keeps each answer conditioned only on the document and its own question. We implement it in SGLang by adding about 80 lines to three existing files and a small engine package; the mask needs no dense tensor, and every decode step has a fixed shape that is captured as a CUDA graph.

With CodeLlama-7B, 8k–16k-token documents and 64–256 questions per document, Parallel Prompting is 3.7× faster than stock SGLang with its FlashInfer backend on an A100 and 5.9–6.0× faster on an RTX A6000, on two SGLang releases. Timed on identical inputs, it is faster than stock SGLang in all 144 runs.

How it works

A serving engine already stores a shared document's KV cache once, but each question is still its own request, so every decode step reads the whole document once per question. Parallel Prompting puts P questions into one request and makes two small changes, so each answer is the same as decoding its question alone:

Three ways to answer several questions about one document. Batched requests read the document once per question per step; batch prompting reads it once but answers attend to each other; Parallel Prompting reads it once per step for all streams and keeps every answer independent.
Three ways to answer several questions about one document, at one decode step. Batch prompting also reads the document once, but its answers attend to each other; Parallel Prompting's stream mask keeps every answer independent.
  1. A stream mask. Every KV slot carries a stream id (−1 for the document). A token of stream s attends only to the document and to earlier tokens of stream s. The rule is added to the causal mask inside SGLang's Triton attention kernel, so no dense mask is ever built.
  2. Shared positions. Every stream's positions start right after the document, as if its question were the only one.

Each decode step appends one token per stream and has a fixed shape, so it runs as one CUDA graph, and the document's KV is read once per step for all P streams.

The in-kernel stream mask at one decode step for a 3-token document and 3 streams.
The mask at one decode step: a 3-token document and P = 3 streams. The new token of stream 1 sees only the document and stream 1's own slots.

More results

The gain grows with document length and the number of questions, from about 1× for a 512-token document with 16 questions to 5–6× at 8k–16k tokens with 256.

Speedup grows with document length and number of questions per document.
Speedup over the faster stock configuration as the document and the number of questions grow (128-token answers).

The gain shrinks for long answers, because each stream also scans its sibling streams' answer tokens. At 1,024-token answers it is still about 2×.

The best number of streams per request is 16–64 on every setting.

Speedup falls as answers get longer.
Speedup as answers get longer (8k-token document, 128 questions).

Real benchmarks

The whole benchmark, timed from an empty cache including document prefill, with Qwen2.5-7B-Instruct on an A100 and every configuration using SGLang's prefix cache.

ConfigurationQuALITYAccuracyLooGLERecall
Stock SGLang, FlashInfer133.2 s76.13403.6 s83.41
Stock SGLang, Triton155.9 s76.22568.0 s83.77
Parallel Prompting73.6 s75.89508.3 s83.61

On QuALITY, whose answers are short, Parallel Prompting is 1.81× faster than the fastest stock setup. LooGLE's answers are long (234 tokens on average), which shrinks the gain: Parallel Prompting is 1.12× faster than stock Triton, which uses the same attention kernels, but slower than stock FlashInfer. Answer quality matches stock SGLang on both, and on SQuAD every paired confidence interval on the F1 difference contains zero.

Continuous batching

The engine can also refill its batch as requests finish, instead of waiting for the slowest one. This pays off when answer lengths differ: on QuALITY with every other article asking for an explanation, it needs 32% fewer decode steps and is 1.22× faster, with accuracy within 0.2 points. When all answers are equally long, it changes nothing.

Citation

@misc{zhao2026readonce,
  title  = {Read Once, Answer Many: Parallel Prompting for
            Shared-Context {LLM} Inference},
  author = {Zekun Zhao and Jeffrey Flanigan},
  year   = {2026},
}