← Back to list

Top 10 Reranker Plays Under 150 ms

Practical SPLADE & ColBERT tactics that keep quality high while your p95 stays snappy.

Thinking Loop · 2025-09-12 16:31 · 3 claps · 4.7 min read
#reranking #splade #colbert #search-performance #information-retrieval
Open on Medium ↗

Top 10 Reranker Plays Under 150 ms

Practical SPLADE & ColBERT tactics that keep quality high while your p95 stays snappy.

Ten battle-tested reranker strategies — SPLADE, ColBERT, caching, adaptive k, quantization, and more — to hit <150 ms end-to-end without sacrificing relevance.

You’ve got the classic two-stage setup: fast ANN recall, then a smarter reranker to separate “good” from “actually useful.” It works — until traffic climbs and your p95 spikes. The fix isn’t hand-waving. It’s a set of specific choices that trade compute for certainty. Below are ten plays I lean on to keep SPLADE and ColBERT reranking tidy, predictable, and under 150 ms door-to-door.

1) Budget First, Then Tune

Before touching models, lock a latency envelope. A simple split that works in production:

  • ANN retrieval: 35–50 ms (top-k = 100–200)
  • I/O + feature prep: 10–20 ms
  • Reranker compute: 60–80 ms (batch 16–32)
  • Response + overhead: 10–20 ms

That’s your 150 ms. Every tweak below protects this budget.

2) Adaptive k with Score Margins

Most queries don’t need a huge candidate set. Use an adaptive top-k from your retriever based on the score margin between the last kept hit and the tail. Big margin? Keep k=80. Tight margin? Raise to k=160 for safety. This alone can shave 20–30 ms downstream.

def choose_k(scores, k_lo=80, k_hi=160, margin=0.08):
    s = sorted(scores, reverse=True)
    return k_lo if (s[k_lo-1] - s[min(len(s)-1, k_hi-1)]) > margin else k_hi

Why it works: rerankers scale roughly with O(k * tokens). Smaller k → faster.

3) SPLADE: Make Sparse Cheap, Not Noisy

SPLADE shines because it’s sparse: you can store document vectors in a standard inverted index and score with efficient postings. Three practical moves:

  • Top-n term pruning: keep the top 40–60 activated terms per document at index time.
  • Value clipping: cap term weights to a sensible max (e.g., 3–5) to stabilize scoring and SIMD utilization.
  • Query-time gating: if the query activation mass is low (super short queries), fall back to BM25+RM3 or expand minimally — don’t waste a full SPLADE forward.

Expect 10–20% latency savings with negligible MAP/NDCG impact when pruning is tuned on held-out queries.

4) ColBERT: Tame Tokens, Not Just k

ColBERT’s late-interaction MaxSim is potent but token-heavy. Control it:

  • Shorten inputs: cap to 180–220 wordpiece tokens for docs/passages; keep query ≤ 32–48.
  • Passage windows: chunk long docs into 128–256 token windows with 32–64 overlap and index windows, not whole docs.
  • Lightweight backbone: Distilled or small-head variants maintain recall with ~30–40% lower compute.

The combo typically cuts 30+ ms without a measurable drop in Recall@10.

5) Quantize Where It’s Safe

Two flavors:

  • ColBERT embeddings: INT8 or FP16 for token vectors (with per-channel scales) maintains MaxSim order while shrinking memory and memory bandwidth.
  • SPLADE weights: quantize activation weights to FP16 (often lossless in practice for ranking).

Memory bandwidth is the secret latency killer. Quantization reduces cache misses and shortens the scoring tail.

6) Precompute Everything You Can

Pipeline friction is real. Remove it:

  • Tokenization cache: cache query tokenization for hot templates (you’ll be surprised how many repeat).
  • ColBERT Q-embeddings: short-lived cache (30–120 s) for popular queries.
  • SPLADE query activations: same idea; tiny footprint, big payoff.

A 30–50% hit rate on these caches often drops p50 by 10–15 ms and smooths p95.

7) Batch for Throughput, Cap for Tail

Rerankers love batching; users hate waiting. The pattern: micro-batches.

  • Queue for up to 3–6 ms to aggregate 16–32 candidates across requests.
  • Hard cap the queue time: never block beyond the limit even if the batch is small.
  • Keep per-batch token caps (e.g., ≤ 6k tokens) to avoid GPU/CPU thrash.

You’ll lift throughput while keeping the tail predictable.

8) Early Exit with Confidence Thresholds

Don’t rerank when the answer is obviously good — or obviously missing.

  • If top ANN scores show a clear gap, skip reranking and return.
  • If the best candidate after a light pass (e.g., BM25+small MLP) is below a quality floor, return “no confident match” instead of spending 70 ms to confirm it.

Real systems save 10–25% of reranker calls this way, particularly on navigational queries.

9) Hybrid Fusion Without Double Work

Ensembles are great; duplicates are not. Use Reciprocal Rank Fusion (RRF) or weighted fusion between BM25/SPLADE/ANN to pick the top-k unique candidates before the heavy model runs. Dedup by doc ID; carry forward only one copy with the best fused score. That’s fewer items for ColBERT or your SPLADE scorer.

10) Profile the Real Hot Path

Let’s be real: the model isn’t always the villain. Latency often hides in:

  • I/O: fetching doc snippets or windows
  • (De)serialization: JSON → tensors and back
  • Python↔C++ hops: tiny, frequent calls

Instrument timings per stage and fix the worst offender first. Teams routinely claw back 20–40 ms from non-model overhead.

A compact control loop (time-aware reranking)

Below is a minimal pattern that respects a time budget; it downgrades work gracefully as the deadline approaches.

import time

BUDGET_MS = 150
STAGES = {
  "retrieval": 50,
  "features": 20,
  "rerank":   70,
  "margin":   10,  # buffer
}

def rerank_with_budget(query, retriever, reranker, now_ms=lambda: time.time()*1000):
    t0 = now_ms()

    # 1) Fast recall
    cand, scores = retriever(query, k=200)
    elapsed = now_ms() - t0
    left = BUDGET_MS - elapsed

    # 2) Adaptive k based on margin & time left
    k = 80 if margin_ok(scores) and left > 90 else 160
    cand = cand[:k]

    # 3) If little time left, pick fast path
    if left < 70:
        return lightweight_rank(query, cand)      # e.g., BM25 or tiny MLP

    # 4) Full reranker with micro-batching
    return reranker(query, cand, batch_tokens=6000, max_wait_ms=5)

Why it works: You align work with a deadline, not a wish.

Quick notes: SPLADE vs. ColBERT under 150 ms

  • SPLADE is fantastic when you need CPU-friendly scoring and inverted-index tricks. It excels on shorter queries and web-style corpora. Pruned activations + caching make it a predictable workhorse.
  • ColBERT wins on semantic nuance and long queries. Token caps, passage windows, and quantization make it very workable under 150 ms, especially with micro-batches.

Both models benefit from the same operational discipline: adaptive k, batching, precompute, and early exits.

A tiny checklist you can run today

  • Fix a latency budget and enforce it.
  • Turn on adaptive k from retrieval.
  • Cap tokens (query & doc); window long docs.
  • Add INT8/FP16 where safe.
  • Cache tokenization and query embeddings for hot terms.
  • Micro-batch with a strict max wait.
  • Add confidence-based early exit.
  • Fuse candidates, dedup before rerank.
  • Profile I/O and serialization; kill obvious waste.
  • Re-measure p50/p95 weekly; regressions get rolled back.

Closing

Speed isn’t a vibe; it’s a budget. When you decide upfront where the milliseconds go, SPLADE and ColBERT stop feeling “heavy” and start feeling surgical. You might be wondering if you’ll lose quality. In practice, these plays preserve or improve relevance because you cut waste, not signal. The trick is to test each lever in isolation, then stack them carefully.

If you want this broken into a runnable benchmark harness (with p50/p95 plots and a CSV of each knob’s impact), tell me what stack you’re on and I’ll tailor a version.


메타데이터
post_id
97d0d00d9442
slug
top-10-reranker-plays-under-150-ms-97d0d00d9442
url
https://medium.com/@ThinkingLoop/top-10-reranker-plays-under-150-ms-97d0d00d9442
canonical_url
https://medium.com/@ThinkingLoop/top-10-reranker-plays-under-150-ms-97d0d00d9442
author_url
https://medium.com/@ThinkingLoop
status
ok
fetched_at
2026-07-17 14:15:42