Bỏ qua để đến nội dung
Search & RAG

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).

tổng thời gian rerankkbatch_size×tbatch\text{tổng thời gian rerank} \approx \left\lceil \frac{k}{\mathrm{batch\_size}} \right\rceil \times t_{\text{batch}}

trong đó tbatchf(batch_size, seq_len, kıˊch thước model)t_{\text{batch}} \approx f(\mathrm{batch\_size},\ \mathrm{seq\_len},\ \text{kích thước model}).

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êntuyến tínhrecall 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 modelgầ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_sizegiả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áchLà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-poolChia 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àiChọn reranker có giới hạn token lớnTrả 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ọi
k = 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.

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ơnNhanh gần tỷ lệ thuậnChấ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 GPU2–3× trên các reranker lớnRấ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 CPUMấ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 đợiThê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 CPUBậc độ lớn với k lớnTiề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ếnSố đ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-fp16torch-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? → float16
chạy CPU, chấp nhận giảm nhẹ chất lượng? → openvino-qint8
chạy CPU trên Intel? → openvino
còn lại → onnx-O3

Danh 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 ms
rerank k=100 ████ phần đáng kể, và là phần BẠN ĐIỀU CHỈNH ĐƯỢC
LLM 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.

Phần 5 — Reranker