LLM Cost Controls: Quantization, Caching, and Batching

See how quantization reduces model memory, while prefix caching avoids repeated prefill work and batch limits bound memory in a self-hosted LLM service.

For a self hosted llm, the practical vLLM cost controls are quantization, prefix caching, and bounded batching rather than a single cheapest setting. The vLLM quantization documentation describes trading model precision for a smaller memory footprint, Automatic Prefix Caching can skip reusable prefill computation, and context and batch-size limits constrain admitted work; none of these mechanisms by itself guarantees a lower provider invoice.

Prepare the hardware and budget for a self hosted llm

Start with a format-to-hardware check. According to the current vLLM quantization compatibility table, the documented support is:

Format Documented hardware support
AWQ Not supported on Volta or AMD GPUs; supported on Turing, Ampere, Ada, Hopper, Intel GPU, and x86 CPU
llm-compressor FP8 (W8A8) The chart marks Ada, Hopper, and AMD GPU support only
GGUF Supported on NVIDIA GPUs from Volta through Hopper and on AMD GPUs; not on Intel GPUs or CPUs
bitsandbytes Supported on NVIDIA GPUs from Volta through Hopper only

Treat this as a compatibility check, not a ranking. The documentation warns that the chart can change as vLLM expands support, so confirm the current entry before building around a format.

Build the provider budget separately from the technical resource estimate. Read the provider’s current pricing and billing documentation, then collect only the line items that appear in your quote or invoice:

  • The compute or GPU rate, its billing unit, and the quantity your configuration will consume.
  • Model-artifact storage charges, if storage is billed separately.
  • Network transfer or egress charges, if listed.
  • Every other recurring or one-time charge shown in the quote.

Fill the provider’s own rate and quantity into the calculation:

COMPUTE_COST = PROVIDER_RATE × BILLED_COMPUTE_QUANTITY

BEFORE_COST = BEFORE_COMPUTE_COST + BEFORE_STORAGE_COST + BEFORE_TRANSFER_COST + BEFORE_OTHER_CHARGES

AFTER_COST = AFTER_COMPUTE_COST + AFTER_STORAGE_COST + AFTER_TRANSFER_COST + AFTER_OTHER_CHARGES

COST_CHANGE = AFTER_COST - BEFORE_COST

A negative COST_CHANGE represents lower calculated spend, while a positive value represents higher spend. Use the same billing period and units on both sides. Do not treat lower GPU memory use as a monetary saving until you have mapped it to the provider’s actual billing units and confirmed whether the configuration changes GPU count, runtime, storage, or another chargeable quantity.

Quantization: reduce model and KV-cache memory

The FP8 W8A8 guide documents hardware acceleration on GPUs such as NVIDIA H100 and AMD MI300x. It also distinguishes native FP8 computation, which requires NVIDIA compute capability 8.9 or higher, from weight-only W8A16 execution through Marlin kernels on Turing and Ampere GPUs.

For a BF16 or FP16 model that should be quantized when loaded, append the documented argument:

--quantization="fp8_per_tensor"

The equivalent vLLM constructor key is quantization="fp8_per_tensor". This online flow does not require calibration data.

The same guide uses LLM Compressor when you want to create and save a quantized model first. Keep vLLM and llm-compressor in separate Python environments, then install the documented package:

pip install llmcompressor

The documented FP8 recipe targets Linear layers while excluding lm_head:

recipe = QuantizationModifier(
    targets="Linear",
    scheme="FP8_DYNAMIC",
    ignore=["lm_head"],
)

The FP8_DYNAMIC flow requires no calibration data. After the model has been saved, load its local directory with:

llm = LLM("./Meta-Llama-3-8B-Instruct-FP8-Dynamic")

These are model or process configuration arguments, not documented API endpoints.

A successful load does not settle model quality. Follow the evaluation guidance in the FP8 guide and include:

add_bos_token=True

The lm_eval tool does not add a bos token by default, and quantized models can be sensitive to its presence.

If the memory target includes the KV cache rather than only model weights and activations, the quantized KV cache guide documents this vLLM setting:

kv_cache_dtype="fp8"

It stores the KV cache in FP8 to reduce its memory footprint. This setting is separate from deciding whether incoming requests can reuse an existing prefix.

Caching: reuse repeated prefixes

Set the following engine setting to enable Automatic Prefix Caching:

enable_prefix_caching=True

The APC documentation explains that vLLM reuses cached KV data when a later query shares a prefix with an existing query. This pattern is most relevant to repeated questions over the same long document and multi-round conversations that reuse earlier chat history.

APC reduces the prefilling phase only. It does not reduce the time spent generating new tokens during decoding. It also provides no performance gain when long answers dominate the request or when a new query shares no prefix with earlier queries. A workload made almost entirely of unrelated prompts therefore gives APC little opportunity to avoid computation.

For your own validation, replay requests that share a controlled prefix and compare them with requests whose prefixes differ. Do not infer a cache hit from response text alone: the APC guidance does not define a cache-hit endpoint or observation field, so check the current vLLM observability documentation before using a particular metric.

Batching: bound context and batch pressure

The vLLM memory guide identifies max_model_len and max_num_seqs as ways to reduce memory use. The first limits model context; the second sets the maximum batch size. Set placeholder values from your actual request and concurrency requirements:

max_model_len=YOUR_VALUE
max_num_seqs=YOUR_VALUE

A smaller max_num_seqs reduces batch pressure, but it is not automatically a better setting. Use request lengths, concurrency, memory readings, and your own acceptance criteria to choose the value. Likewise, set max_model_len from the prompt-plus-output shape your application must support rather than copying a model’s largest advertised context.

The vllm serve reference documents these relevant serving arguments:

--max-model-len=auto
--gpu-memory-utilization=YOUR_VALUE
--kv-cache-memory-bytes=YOUR_VALUE

--max-model-len covers prompt and output tokens. If it is unset, vLLM derives the value from the model configuration; -1 or auto selects the largest model length that fits in GPU memory. Because auto maximizes the available context rather than minimizing spend, set an explicit workload-based limit when context containment matters.

The documented default for --gpu-memory-utilization is 0.92, and this is a per-instance limit. When --kv-cache-memory-bytes is set, it directly controls KV-cache memory and overrides the GPU-memory-utilization setting. Do not combine these arguments without accounting for that override.

Validate the rollout and calculate the result

Keep model identifier, hardware, quantization setting, cache configuration, context limit, batch limit, prompt shape, output requirement, and provider billing unit in each validation record. Change a single control per run, then compare output quality, request acceptance, memory readings, and billed quantities against your baseline.

Treat hardware mismatch, unrelated-prefix traffic, an unnecessarily large context limit, and overlapping memory controls as configuration errors to resolve before attributing a saving to any technique. The useful result is not simply “less memory used”; it is lower calculated spend without unacceptable output changes or failed requests.

No commands or models were run for this article. These steps are compiled from the official documentation linked in the text and should be checked against the current version before use.

Sources