5.9 Latency và throughput: chỗ reranker tốn tiền
Reranker là tầng đắt nhất trong pipeline retrieval, và nó đắt theo một cách rất dễ dự đoán — nghĩa là bạn tính trước được, và điều chỉnh được. Mục này về bốn núm điều chỉnh và một cái bẫy.
Để có mốc so: BM25 trên corpus 50 doc của cẩm nang mất 0,03 ms/truy vấn (baseline đã đo). Mọi thứ dưới đây tốn nhiều hơn con số đó hàng nghìn lần. Đó là cái giá của việc để câu hỏi và tài liệu nhìn thấy nhau (5.2).
Công thức
Phần tiêu đề “Công thức”trong đó .
Bốn biến bạn kiểm soát được, và ảnh hưởng của từng biến khác nhau rõ rệt:
| Biến | Ảnh hưởng lên latency | Ảnh hưởng lên chất lượng |
|---|---|---|
k — số ứng viên | tuyến tính | recall bão hoà, có thể giảm khi k quá lớn (5.13) |
seq_len — độ dài cặp (query + doc) | siêu tuyến tính (attention ~ O(L²)) | cắt ngắn quá → mất phần văn bản chứa câu trả lời |
| kích thước model | gần tuyến tính theo số tham số | lớn hơn thường tốt hơn, lợi ích giảm dần |
batch_size | giảm tổng thời gian (song song hoá) | không ảnh hưởng |
Hai dòng đầu là chỗ đáng đầu tư suy nghĩ nhất, vì chúng đánh đổi với chất lượng.
Núm 1 — k: tuyến tính, hiển nhiên, và bị đặt bừa nhiều nhất
Phần tiêu đề “Núm 1 — k: tuyến tính, hiển nhiên, và bị đặt bừa nhiều nhất”Chi phí rerank tỷ lệ thuận với k. k = 200 đắt gấp đôi k = 100, không có tối ưu
nào thoát được điều đó, vì mỗi cặp là một forward pass độc lập
(5.2).
Trong khi đó recall@k bão hoà. Nên tồn tại một điểm mà bạn trả gấp đôi để mua gần như không gì — cách tìm điểm đó ở 5.4.
k là núm đầu tiên nên xoay khi vượt budget latency, không phải núm cuối.
Núm 2 — seq_len: cái bẫy đắt nhất và vô hình nhất
Phần tiêu đề “Núm 2 — seq_len: cái bẫy đắt nhất và vô hình nhất”Self-attention có chi phí bậc hai theo độ dài chuỗi. Nghĩa là chunk dài không chỉ đắt hơn tỷ lệ thuận, nó đắt hơn nhanh hơn thế:
cặp 128 token → chi phí ~1× (đơn vị quy chiếu)cặp 256 token → chi phí ~4×cặp 512 token → chi phí ~16×(Toán tay từ O(L²) của attention. Trên phần cứng thật, hằng số và các phần chi phí tuyến tính khác — MLP, tải trọng số — làm đường cong phẳng hơn con số này; đây là giới hạn trên để bạn thấy hình dạng, không phải số đo.)
Đi kèm là hai chuyện phải xử lý:
Tài liệu dài hơn giới hạn của model. Mỗi reranker có giới hạn số token cho một cặp. Vượt quá thì model cắt phần đuôi — im lặng. Nếu câu trả lời nằm ở đoạn bị cắt, điểm sẽ thấp và bạn không có cách nào biết. Ba lối xử lý:
| Cách | Làm gì | Đánh đổi |
|---|---|---|
| Chunk nhỏ hơn ngay từ đầu | Đưa reranker các đoạn ngắn, đúng cỡ | Sạch nhất. Cần thống nhất với chiến lược chunking (Tầng 4) |
| Cắt cửa sổ rồi max-pool | Chia tài liệu dài thành cửa sổ, chấm từng cửa sổ, lấy điểm cao nhất làm điểm tài liệu | Đúng về mặt ngữ nghĩa, nhân chi phí lên theo số cửa sổ |
| Dùng model ngữ cảnh dài | Chọn reranker có giới hạn token lớn | Trả bằng chi phí bậc hai ở trên |
Tiếng Việt tốn nhiều token hơn. Cùng một lượng thông tin, văn bản tiếng Việt thường ngốn nhiều token hơn tiếng Anh trên các tokenizer phổ biến (dấu và âm tiết bị chẻ nhỏ — xem 3.1). Hệ quả trực tiếp: giới hạn token siết chặt hơn bạn tưởng, và ngân sách latency cũng vậy. Đo số token thật trên corpus của bạn bằng tokenizer của chính model reranker, đừng suy từ số ký tự.
Núm 3 — batch: cách mua lại latency gần như miễn phí
Phần tiêu đề “Núm 3 — batch: cách mua lại latency gần như miễn phí”k forward pass tuần tự là cách chậm nhất có thể. GPU rảnh phần lớn thời gian vì mỗi
cặp quá nhỏ để lấp đầy nó.
k = 100, tuần tự: 100 × t ← GPU chờ giữa các lần gọik = 100, batch 32: 4 × T ← T > t nhưng T ≪ 32tĐây là lý do chênh lệch giữa một triển khai reranker nghiệp dư và một triển khai đúng có thể là một bậc độ lớn, cùng model, cùng phần cứng. Ba điều cần biết:
- Batch phải sắp theo độ dài. Trong một batch, mọi chuỗi bị đệm (pad) tới độ dài của chuỗi dài nhất. Trộn một đoạn 512 token với ba đoạn 64 token nghĩa là bạn trả tiền cho 4 × 512. Xếp các cặp có độ dài gần nhau vào cùng batch.
- Batch quá lớn không giúp thêm. Qua điểm lấp đầy GPU, tăng batch chỉ tăng latency của batch mà không tăng throughput.
- Chọn batch theo p95, không theo trung bình. Điều bạn hứa với người dùng là đuôi phân phối.
Núm 4 — model và cách chạy nó
Phần tiêu đề “Núm 4 — model và cách chạy nó”Từ đắt tới rẻ, cùng một chất lượng mục tiêu:
| Đòn | Được gì | Mất gì |
|---|---|---|
| Model ít tham số hơn | Nhanh gần tỷ lệ thuận | Chất lượng giảm — phải đo. Một ablation cho thấy tỉa Mistral-7B từ 32 xuống 16 lớp chỉ mất 1% độ chính xác cho một nửa số tham số |
| fp16 trên GPU | 2–3× trên các reranker lớn | Rất ít chất lượng. Đây là đòn đầu tiên nên làm nếu bạn chạy GPU |
| Quantize int8 trên CPU (OpenVINO) | Nhanh hơn đáng kể trên CPU | Mất một chút chất lượng — chấp nhận được ở tầng giữa của cascade |
| Server chuyên dụng thay vì tự viết loop | Được batch động, xếp theo độ dài, quản lý hàng đợi | Thêm một dịch vụ trong hệ thống. Chú ý ràng buộc kiến trúc của TEI/vLLM (5.8) |
| GPU thay CPU | Bậc độ lớn với k lớn | Tiền, và độ phức tạp vận hành (5.10) |
Ba điều phản trực giác, từ một bộ benchmark cross-encoder được đo lại tháng 7/2026 — đáng đọc vì chúng bác bỏ những lời khuyên vẫn đang được nhắc lại khắp nơi:
| Lời khuyên phổ biến | Số đo 7/2026 nói gì |
|---|---|
| “Export sang ONNX để chạy nhanh hơn trên GPU” | Không còn đúng. Tỷ lệ tăng tốc của ONNX đã tụt về mức hoà (0,9–1,1×): đồ thị đã export là tĩnh nên không thừa hưởng các cải tiến của runtime PyTorch. Mặc định tốt hơn cho GPU hiện nay là PyTorch + fp16 |
| “fp16 nhanh hơn, bật ở đâu cũng được” | Trên CPU thì tuyệt đối không. torch-fp16 và torch-bf16 chạy ở khoảng 0,17× — tức chậm hơn gần 6 lần |
| “Bật FlashAttention-2 cho nhanh” | Không dùng cho cross-encoder. Phần unpadding chỉ áp dụng cho feature-extraction; head phân loại không tương thích với input đã làm phẳng. Đo được: ngang fp16 trên văn bản dài và chậm hơn 20–45% trên văn bản ngắn, trên cả bốn reranker được thử |
Cây quyết định rút ra từ cùng bộ số đo đó:
chạy GPU? → float16chạy CPU, chấp nhận giảm nhẹ chất lượng? → openvino-qint8chạy CPU trên Intel? → openvinocòn lại → onnx-O3Danh sách model cụ thể qua được cửa “chạy CPU + có tiếng Việt”, kèm ngân sách RAM và cảnh báo về VNNI, ở 5.8 — Không có GPU.
Về dòng cuối: CPU không phải lựa chọn sai mặc định. Với k nhỏ (10–25), model nhỏ,
đoạn ngắn và lưu lượng thấp, một reranker quantize chạy CPU có thể nằm gọn trong budget
và tiết kiệm cả một GPU. Ngưỡng cần chuyển sang GPU phụ thuộc k × seq_len × QPS của
bạn — đo trên hạ tầng thật.
Ngân sách latency: đặt reranker vào bức tranh tổng
Phần tiêu đề “Ngân sách latency: đặt reranker vào bức tranh tổng”Một truy vấn RAG thật, và chỗ reranker chiếm:
retrieval (BM25 + ANN) ▏ ~5–20 msrerank k=100 ████ phần đáng kể, và là phần BẠN ĐIỀU CHỈNH ĐƯỢCLLM sinh câu trả lời ████████████████ thường lớn nhất, và bạn ít kiểm soát nhấtĐiều này dẫn tới một kết luận thực dụng mà nhiều người bỏ qua: nếu LLM của bạn mất vài giây để sinh câu trả lời thì thêm vài chục ms rerank là không đáng kể với người dùng. Ngân sách chặt chỉ xuất hiện ở hai chỗ:
- Search hiển thị trực tiếp (gõ là thấy kết quả, không có LLM ở giữa) — ở đây từng chục ms là thật.
- Agent multi-hop gọi retrieval nhiều lần trong một lượt — chi phí rerank nhân với số lần gọi, và đây là chỗ nó âm thầm phình ra (Tầng 9).
→ Nên áp dụng vào AI Agent: đo p95 của chỉ tầng rerank, tách khỏi tổng, ở k thật
và độ dài chunk thật của bạn. Rồi đặt một timeout cứng nhỏ hơn budget và một đường
degrade về thứ tự stage 1 (5.11). Không có timeout thì
một hôm nào đó reranker chậm sẽ thành một sự cố toàn hệ thống.