Bạn vừa quantize xong Llama-3-8B Q4_K_M (bài 22). Giờ cần serve cho 100 user đồng thời. Câu hỏi: dùng cái gì?

Trả lời nhanh sai: “Ollama, nó dễ”. Cũng có thể là vLLM, llama.cpp server, TGI, SGLang, hoặc bitnet.cpp tuỳ context. Mỗi framework tối ưu cho một use case rất khác nhau. Chọn sai = trả gấp 5 lần chi phí hoặc trải nghiệm user tệ.

Bài này so sánh 4 framework phổ biến nhất theo các trục quan trọng nhất: throughput, latency, hardware support, ops complexity.

Dành cho: dev đã quantize model thành công, đang chọn engine để deploy production hoặc internal tool.

Mental model: serving không phải “chạy model”

Người mới hay nghĩ serving LLM là “load model lên GPU rồi gọi model.generate()”. Đúng nhưng không đủ.

Production serving có những vấn đề mà naive approach không xử lý:

  • Concurrency: 100 request đồng thời, mỗi cái có context khác nhau, làm sao share GPU hiệu quả?
  • Memory: KV cache tốn RAM hơn cả model weights. Quản lý sao cho không OOM?
  • Latency vs throughput: ưu tiên trả lời nhanh từng request (latency thấp) hay xử lý nhiều request mỗi giây (throughput cao)?
  • Streaming: trả từng token một thay vì đợi hết câu trả lời?
  • Batching: gộp nhiều request vào một batch để GPU tính song song?

Mỗi framework có triết lý khác nhau cho 5 câu hỏi này. Khác biệt 50 lần throughput xuất phát từ đây.

Phần 1: 4 framework, 4 triết lý

vLLM: built for server, GPU. Triết lý: tối đa throughput bằng PagedAttention và continuous batching. Sinh ra để serve hàng nghìn request/giây trên GPU cluster.

llama.cpp (server mode): built for flexibility, chạy được trên CPU + GPU + Apple Silicon. Triết lý: portable, hỗ trợ GGUF, tối ưu cho 1-10 user concurrent.

Ollama: built for developer experience, single-user. Triết lý: 1 command để chạy bất kỳ model nào local. Wrapper trên llama.cpp + thêm model registry.

bitnet.cpp: built for BitNet models trên CPU. Triết lý: chạy được model lớn (70B) trên CPU thường, không cần GPU. Hệ sinh thái còn non.

Đối chiếu nhanh:

TrụcvLLMllama.cppOllamabitnet.cpp
Hardware chínhNVIDIA GPUCPU + GPU + M-seriesCPU + GPU + M-seriesCPU
Format modelHF, AWQ, GPTQGGUFGGUF (via llama.cpp)GGUF (BitNet-specific)
Concurrent users1000+1-201-51-10
Throughput (8B Q4)2000+ tok/s30-50 tok/s30-50 tok/s50-80 tok/s
Latency first token~50ms~200ms~200ms~200ms
Setup khó/dễTrung bìnhDễRất dễTrung bình
Production-readyCó (cần wrap)Không (single user)Không (vẫn experimental)

Phần 2: vLLM, vũ khí throughput

vLLM (UC Berkeley, 2023) ra đời vì các engine truyền thống lãng phí GPU memory cho KV cache. Giải pháp: PagedAttention, mượn ý tưởng từ virtual memory của OS, chia KV cache thành các “page” nhỏ và phân bổ động.

Lợi ích: cùng GPU, vLLM phục vụ được nhiều request đồng thời hơn 3-5 lần so với engine truyền thống (HF Transformers, TGI v1).

Cách dùng:

pip install vllm

# Serve Llama-3-8B AWQ
vllm serve TheBloke/Meta-Llama-3-8B-Instruct-AWQ \
    --quantization awq \
    --max-model-len 8192 \
    --gpu-memory-utilization 0.9 \
    --tensor-parallel-size 1

API tương thích OpenAI:

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")
response = client.chat.completions.create(
    model="TheBloke/Meta-Llama-3-8B-Instruct-AWQ",
    messages=[{"role": "user", "content": "Viết quicksort"}],
    stream=True
)
for chunk in response:
    print(chunk.choices[0].delta.content, end="")

Flags quan trọng cần biết:

FlagÝ nghĩaMặc định nên dùng
--gpu-memory-utilization% VRAM cho model + KV cache0.85-0.9
--max-model-lenContext window tối đaĐặt theo use case, không max model
--tensor-parallel-sizeSplit model qua N GPU1 nếu model fit 1 GPU
--max-num-seqsSố request concurrent tối đa256 (default)
--enable-prefix-cachingCache prefix chung của các requestBật nếu có system prompt dài

Pitfall vLLM hay gặp: --max-model-len đặt theo model max (128k cho Llama-3) sẽ chiếm hết VRAM cho KV cache theoretical, làm batching giảm. Đặt theo thực tế use case (4096 hoặc 8192).

Phần 3: llama.cpp, dao đa năng

llama.cpp (Georgi Gerganov, 2023) bắt đầu là project chạy LLaMA trên Macbook M1. Giờ là engine inference portable nhất tồn tại: chạy được trên Linux x86, Mac M-series, Windows, Android, iOS, thậm chí Raspberry Pi.

Tốc độ trên GPU thấp hơn vLLM nhưng tốc độ trên Apple Silicon và CPU thì khó ai sánh.

Server mode:

git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp && make LLAMA_METAL=1   # Mac M-series
# hoặc
make LLAMA_CUDA=1   # NVIDIA GPU

# Serve
./server -m ./models/llama-3-8b-q4km.gguf \
    --host 0.0.0.0 --port 8080 \
    -c 8192 -t 8 -ngl 99

Flags quan trọng:

FlagÝ nghĩa
-c 8192Context length
-t 8Số CPU thread
-ngl 99Số layer offload lên GPU (99 = tất cả)
-cbContinuous batching
--parallel 4Số slot song song

llama.cpp v2024+ có continuous batching giống vLLM nhưng đơn giản hơn. Throughput thấp hơn nhưng đủ cho 1-20 concurrent user.

Phần 4: Ollama, developer experience

Ollama là wrapper UX trên llama.cpp. Đóng góp chính: model registry kiểu Docker Hub, command đơn giản, auto-detect hardware.

# Install
curl -fsSL https://ollama.com/install.sh | sh

# Pull và chạy
ollama pull llama3:8b-instruct-q4_K_M
ollama run llama3:8b-instruct-q4_K_M

# REST API
curl http://localhost:11434/api/generate -d '{
  "model": "llama3:8b-instruct-q4_K_M",
  "prompt": "Viết function fibonacci",
  "stream": false
}'

Ưu điểm:

  • Một dòng install, một dòng pull, một dòng chạy
  • Hot-swap model: load model A, dùng xong, load model B mà không cần restart
  • Modelfile cho phép customize prompt template

Nhược điểm:

  • Không thiết kế cho production multi-user. Throughput dưới mức llama.cpp server raw vì có overhead wrapper.
  • Không có batching tốt như vLLM
  • Single user / dev tool, không phải serving infrastructure

Quy tắc: Ollama tuyệt vời cho local dev, prototype, internal tool 1-5 user. Production multi-user thì chuyển sang vLLM hoặc llama.cpp server thuần.

Phần 5: bitnet.cpp, lực lượng tương lai

bitnet.cpp (Microsoft, cuối 2024) fork từ llama.cpp, tối ưu cho model BitNet 1.58-bit. Cho phép chạy model 8B-70B trên CPU thường mà không cần GPU.

git clone --recursive https://github.com/microsoft/BitNet
cd BitNet
pip install -r requirements.txt
python setup_env.py --hf-repo HF1BitLLM/Llama3-8B-1.58-100B-tokens -q i2_s

python run_inference.py \
    -m models/Llama3-8B-1.58-100B-tokens/ggml-model-i2_s.gguf \
    -p "Giải thích cách hoạt động của TCP/IP" \
    -n 512 -t 8

Performance lý thuyết: Llama-3-8B BitNet trên CPU desktop ~70 tokens/s. Llama-3-70B BitNet trên Mac M2 Ultra ~10 tokens/s. Đây là điều mà quantization truyền thống không làm được với cùng hardware.

Caveat: số model BitNet open source còn rất ít. Hệ sinh thái fine-tune chưa hoàn chỉnh. Đang ở giai đoạn early adopter.

Phần 6: Quyết định chọn framework

Quy trình ra quyết định:

Câu 1: Bao nhiêu user concurrent?

  > 50 user --> vLLM (production serving)
  10-50 user --> llama.cpp server với continuous batching
  1-5 user --> Ollama hoặc llama.cpp server
  1 user (dev) --> Ollama

Câu 2: Hardware có gì?

  NVIDIA GPU server --> vLLM hoặc llama.cpp CUDA
  Apple Silicon Mac --> llama.cpp Metal hoặc Ollama
  CPU only laptop --> Ollama hoặc bitnet.cpp (nếu model BitNet phù hợp)
  Edge device --> llama.cpp

Câu 3: Yêu cầu khác?

  OpenAI API compatible --> vLLM, llama.cpp server (đều có)
  Streaming --> Tất cả đều có
  Function calling --> vLLM > llama.cpp = Ollama
  Multi-model --> Ollama (hot-swap)

Phần 7: Pitfall thực tế

Pitfall 1: Chọn Ollama cho production rồi đau khổ.

Một dev tôi quen deploy Ollama cho internal tool 30 user, kết quả: queue dài, latency tới 30 giây vì single sequential queue. Chuyển sang vLLM cùng GPU, latency rớt xuống 1 giây.

Ollama tốt cho dev nhưng không phải production server. Đừng dùng sai mục đích.

Pitfall 2: vLLM với context length max model.

Mặc định Llama-3-70B có context 8k, Llama-3-70B-Instruct có 8k, Llama-3.1 có 128k. Nếu --max-model-len 128000, vLLM allocate KV cache space cho 128k context cho mỗi request có thể serve. VRAM bị ăn sạch, batching tệ đi.

Fix: đặt --max-model-len theo use case thực (4096 hoặc 8192 cho chat thông thường), bật --enable-prefix-caching nếu có system prompt dài chia sẻ giữa các request.

Pitfall 3: llama.cpp -ngl set sai.

-ngl (number of GPU layers) quyết định bao nhiêu layer offload lên GPU. Nếu set thấp, model chạy phần lớn trên CPU dù có GPU. Cách check: log của llama.cpp sẽ in llm_load_tensors: offloaded X/N layers to GPU.

Quy tắc: -ngl 99 (offload max), giảm dần nếu OOM. Llama-3-8B Q4 fit gọn trên RTX 3060 12GB với -ngl 99.

Pitfall 4: Quên đo first-token latency vs total time.

User cảm nhận tốc độ qua first-token latency (thời gian từ khi gửi request đến khi nhận token đầu tiên). Throughput cao mà first-token chậm = user vẫn thấy chậm.

vLLM tối ưu cả hai nhưng có thể trade off. Nếu first-token quan trọng, giảm batch size, dùng prefix caching.

Phần 8: Benchmark đáng tin

Thay vì tin vào số quảng cáo, benchmark trên hardware của bạn:

import time
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")

prompt = "Viết một bài hướng dẫn ngắn về Docker, khoảng 500 từ."
start = time.time()
tokens = 0
first_token_time = None

stream = client.chat.completions.create(
    model="llama-3-8b",
    messages=[{"role": "user", "content": prompt}],
    stream=True
)
for chunk in stream:
    if first_token_time is None:
        first_token_time = time.time() - start
    if chunk.choices[0].delta.content:
        tokens += 1

total = time.time() - start
print(f"First token: {first_token_time:.2f}s")
print(f"Total: {total:.2f}s, Tokens: {tokens}")
print(f"Throughput: {tokens / total:.1f} tok/s")

Chạy script này 10 lần liên tiếp, lấy median. So sánh giữa các framework trên cùng model + hardware. Số bạn đo > số người khác claim.

Cheatsheet

Tình huốngFramework
Production serving 100+ user, có GPU NVIDIAvLLM
Dev local, M1/M2/M3 MacOllama
Internal tool 5-20 userllama.cpp server
Chạy model 70B trên laptop 64GB RAM, không GPUbitnet.cpp (nếu có BitNet phù hợp) hoặc llama.cpp Q4_K_M
Cần OpenAI API compatiblevLLM hoặc llama.cpp server
Cần hot-swap nhiều modelOllama
Cần function calling productionvLLM
Edge device / embeddedllama.cpp

Phối hợp:

  • Dev local dùng Ollama để prototype nhanh
  • Production chuyển sang vLLM (nếu GPU) hoặc llama.cpp server (nếu CPU/Mac fleet)
  • Cùng một model GGUF Q4 có thể dùng cho cả 2 nhờ chia sẻ format

Lời kết

Không có framework tốt nhất tuyệt đối. Có framework phù hợp nhất với constraint của bạn. Ai nói “luôn dùng X” là chưa serve qua scale khác production hoặc chưa chạy benchmark.

Hands-on cho bạn:

  1. Install Ollama trên máy bạn, pull llama3:8b-instruct-q4_K_M. Chạy benchmark đo first-token latency và throughput single user.
  2. Nếu có GPU NVIDIA: install vLLM trên cùng máy, serve cùng model (AWQ version), so sánh số.
  3. Mô phỏng concurrent load với locust hoặc wrk: 10 user gửi request cùng lúc. Xem framework nào giữ latency tốt.
  4. Đọc PagedAttention paper (vLLM) ít nhất section 3-4 để hiểu tại sao throughput cao đến vậy.

Bài tiếp theo: KV cache và PagedAttention: tăng throughput inference. vLLM nhanh không phải vì code C++ tốt mà vì hai kỹ thuật memory management. Hiểu chúng giúp bạn debug khi inference chậm bất thường, và viết được engine của riêng mình nếu muốn.