Splink vs. Tilores: A Reproducible Benchmark on 1 Million Records
TL;DR: On 1,000,000 synthetic records with 100,000 known duplicate pairs, Tilores reached F1 0.9949 versus Splink’s 0.8867, a +10.8pp gap driven almost entirely by recall (+19.4pp). Splink wins decisively on raw batch throughput and stays free. The main difference is architectural. Splink produces a one-off pair list, while Tilores builds a persistent, queryable entity index you can search in real time. Every number is reproducible from the published repo.
See it on your data: Book a Demo to walk through real-time entity resolution on your own records, or Get the Evaluation Build to try it locally.
Why benchmark Splink against Tilores at all?
Entity resolution benchmarks usually fall into one of two traps: they run on toy datasets (10k records, single-field matching), or they compare tools that aren’t really competing. The Splink vs. Tilores question is one practitioners actually ask: I’m using Splink for deduplication. At what point, if any, does it make sense to switch?
Splink is the most credible open-source entity resolution library available today: built inside the UK Ministry of Justice, probabilistic, explainable, and free. (We cover the wider field in our roundup of open-source entity resolution libraries.) Tilores is a real-time entity resolution API built for production. Our goal was not to declare a winner, but to find out where the differences actually are, with enough rigour that someone else could check our work. We also wrote up how we built a benchmark we would trust.
All benchmark code, rule configs, and pre-computed results are published here: github.com/tilotech/tilores-splink-benchmark.
How was the benchmark set up?
We generated a 1,000,000-record synthetic dataset with the Faker library (en_GB locale, seed 42): 900,000 unique entities plus 100,000 intentionally corrupted duplicates. For each duplicate, 1–3 random fields receive a character-level corruption (swap, delete, insert, or replace), and 10% of records have one field blanked entirely. The result is a 100,000-pair ground-truth file, and both tools are evaluated against all 100,000 pairs, with no sampling.
Both tools match on the same fields: first_name, last_name, date_of_birth, city, postcode, and phone. Splink uses a probabilistic model with a match threshold of probability ≥ 0.80; Tilores is rule-based and links a pair when any of its six linking rules fires. The full configurations are in the repo.
Splink ran as an embedded DuckDB library on a developer laptop; Tilores ran on a 32 GB LXC container, the same memory class. Neither has a meaningful hardware advantage.
What were the results?
| Metric | Splink (DuckDB) | Tilores | Δ |
|---|---|---|---|
| F1 | 0.8867 | 0.9949 | +10.8pp |
| Precision | 0.9999 | 0.9998 | ~tie |
| Recall | 0.7965 | 0.9900 | +19.4pp |
| True positives (of 100k) | 79,655 | 99,000 | +19,345 |
| False positives | 5 | 15 | +10 (3× more — Splink wins) |
| False negatives | 20,345 | 1,000 | −19,345 |
Precision is near-identical. Both tools are cautious about false matches, and in absolute terms 15 false positives out of 100,000 pairs is negligible (Splink edges this one with 5). The decisive gap is recall: Tilores finds 19,345 more true duplicates. That is the number that matters in practice, because every missed duplicate is a record that stays siloed.
What drives the recall difference?
A 19.4 percentage-point recall gap on a clean, well-structured synthetic dataset is surprisingly large. The reason is worth understanding, because the gap is architectural rather than a matter of tuning.
Splink is a probabilistic record-linkage system. It trains expectation-maximisation parameters from the data, applies a blocking step to limit candidate pairs, and assigns match probabilities. The blocking step is load-bearing: records that never co-appear in the same blocking bucket cannot be matched, regardless of field similarity. Any duplicate that falls outside those buckets, because both postcode and last name are corrupted, say, is a structural false negative. Blocking is fast and necessary, but it is also where most of the recall is lost.
Tilores uses a rule-based graph approach. It evaluates explicit linking rules over phonetic and string-distance comparisons, and the rules overlap: a pair can be linked through a name+DOB rule, a name+city rule, or a postcode rule, with results merged transitively in the entity graph. There is no blocking gate that can prevent a match. If any one rule fires, the records link. The cost is configuration: you enumerate the rules yourself. The benefit is that corruption in one field does not block a match when another field is clean.
You could push Splink’s recall higher by adding blocking keys, but each key expands the candidate space and slows the run. Splink has to manage a real recall-versus-complexity trade-off; Tilores manages it differently, through rule selectivity.
What does the benchmark deliberately not compare?
Batch throughput. Splink is fast at batch deduplication in a way that is hard to match: 1M records in 9.24 seconds as an embedded DuckDB library. For pure offline pipelines with no real-time requirement, that is excellent and costs nothing in infrastructure.
Explainability. Splink produces per-pair match probabilities and can show which fields contributed how much. If an auditor asks why two records were linked, Splink has a native probabilistic answer. Tilores gives you rule provenance (which rule caused a link) but not a weight breakdown. For some regulated workflows, that distinction decides the choice.
Real-world corruption. Our dataset uses synthetic FEBRL-style corruption. Real customer data has different distributions: OCR errors, transliteration noise, locale inconsistencies, missing fields. Synthetic benchmarks are reproducible, but they are not a substitute for testing on your own data.
When should you choose each tool?
| Choose | When |
|---|---|
| Splink | Zero-cost requirement · pure batch · full code-level control of the matching pipeline · explainable probabilistic scores · academic / R&D work where the output is a de-duplicated list. |
| Tilores | Real-time lookups after ingest · incremental ingestion (a single new record via API, not a full re-run) · a persistent searchable entity index · a managed, low-maintenance service with high-availability SLAs and no cluster to run · matching that’s fully configurable in code or a graphical interface · KYC, fraud, or customer-360 at query time. |
| Both | Splink for prototyping and ground-truth labelling, Tilores for production deployment. |
The migration case is the scenario we actually wanted to understand: where does a team using Splink for batch deduplication hit the wall? The wall is usually not accuracy. It is the moment someone asks, “Can we check whether this new record is a duplicate right now, without waiting for the next batch?” That question is architecturally unanswerable with a pair list. Tilores builds a persistent index instead: search latency on the 1M-record index in this benchmark is 8.6ms p95 on LAN, and it sits next to your existing MDM, CDP, warehouse, or KYC stack rather than replacing it.
What are the limitations?
Both configurations are published in full. Splink’s is closer to tutorial-style, and a Splink expert could likely gain another 1–2pp F1 with custom blocking and m/u-value tuning, so the comparison is not the last word on either tool’s ceiling. We note this in the methodology and accept it as a limitation. If someone submits an improved Splink config via pull request, we will re-run and update the results.
The dataset is synthetic. It is reproducible and honest about its construction, but it is not a substitute for your own data.
How do you reproduce this?
# 1. Generate the 1M-record dataset (deterministic, seed 42)
python benchmark/generate_febrl_data.py
# 2. Run Splink — no account required
python benchmark/benchmark_splink_febrl.py
# 3. Run Tilores — requires a running Tilores instance
export TILORES_API_URL=https://YOUR_INSTANCE.svc.tilores.io/YOUR_ID/graphql
export TILORES_TOKEN=your-token-here
python benchmark/benchmark_tilores_febrl.py
The exact Tilores rule configuration is published in the repo, alongside pre-computed results for both tools and the full methodology, evaluation protocol, and caveats. If you find a flaw in the methodology, open an issue. If you can beat either score on the same data with a reproducible config, open a pull request and we’ll publish it.
FAQ
What is entity resolution?
Entity resolution decides whether two records refer to the same real-world entity despite variations in name, address, or identifier. It combines deterministic rules with fuzzy matching to assign each real entity one stable ID, so analytics, fraud checks, and AI all work from a single accurate view.
Is Splink or Tilores more accurate?
On this benchmark Tilores reached higher F1 (0.9949 vs 0.8867), driven by recall: it found 19,345 more true duplicates out of 100,000 pairs. Precision was effectively tied. Accuracy depends on configuration and data, which is exactly why the benchmark is fully reproducible.
Why is Splink’s recall lower in this test?
Splink relies on a blocking step to limit candidate pairs for performance. Duplicates whose blocking fields are both corrupted never become candidates and can’t be matched. Tilores has no blocking gate — overlapping rules can still link a pair when one field stays clean.
Can Splink do real-time lookups like Tilores?
No. Splink produces a one-off pair list during batch processing; there is no persistent query API afterwards. Tilores builds a persistent entity index you can search at query time (8.6ms p95 on a 1M-record index here), which is what real-time KYC and customer-360 workflows need.
Where can I see the benchmark code?
Everything — scripts, the Tilores rule config, and pre-computed results — is in the public repository at github.com/tilotech/tilores-splink-benchmark.
See what resolved entity data does for your business — and your AI.