Deduplicating a billion analytics records with a hash set — which expected-O(1) assumption breaks first?
answer
- the analysis counts probes, not time
- which quantity actually grows with n
- chain length tracks the load factor
- what a billion entries does to cache
- count distinct keys before sizing anything
basics
~20 sMemory and locality break first. At a billion keys the table no longer fits in cache, so every constant-time probe becomes a cache or page miss — a cost the analysis never counted. Collision pileups are rarely the binding constraint.
solid answer
~50 sThe candidates are collision pileups, growth re-placement, and memory. Collisions are the least likely culprit: chain length tracks the load factor, not the entry count, so a billion keys under a decent hash behave exactly like a million. Total re-placement across all inserts is linear, so it is a constant-factor issue, not an asymptotic one. What actually bites is the model itself: expected O(1) counts **probes and comparisons** and assumes every memory access costs the same. At a billion entries the table is tens of gigabytes, no level of cache holds a useful fraction of it, and each probe is a cache miss or worse a page fault — hundreds of times the cost of the probe you analysed. The engineering answer is to budget bytes per entry against the memory ceiling first, and if it does not fit, partition the key space into passes that each do.
go deeper
Take away that a constant-time operation is not a free operation. Big-O counts steps, and one step against a huge table can be far slower than one step against a small one.
Be able to say why chain lengths do not grow with the entry count, and why the total growth re-placement work stays linear. Both rule-outs are needed before you can name the real constraint.
Produce the memory arithmetic unprompted — distinct cardinality times realistic bytes per entry against the ceiling — and recognise the workload as memory-bound, so you measure and reason about bandwidth and misses rather than instruction counts.
Own the call when the exact structure does not fit the budget: extra passes over the key space, a probabilistic structure with a bounded error rate, or more memory. Each has a cost someone must sign for, and framing it as a product decision rather than a tuning exercise is the job.
## The question behind the question Asking which assumption breaks first at scale is really asking whether you know what the O(1) is counting. It is not counting nanoseconds. It is counting operations in the unit-cost RAM model, where fetching any address costs the same as fetching any other. That model is an excellent approximation while the working set fits in cache and a poor one once it does not — and "once it does not" is precisely what a billion-key table guarantees. ## Ruling out the two usual suspects **Collision pileups.** The expected number of entries sharing a bucket is the load factor, and a growth policy holds the load factor roughly constant regardless of `n`. So a table of `10^9` entries has the same expected probe count as one of `10^6`. The longest chain does grow, but only as `log n / log log n` under the uniform model — from single digits to low double digits across three orders of magnitude. Unless the hash is failing to mix the keys, collisions are not what changed. **Growth re-placement.** Every live entry is re-placed each time the table grows, and that feels alarming at a billion entries. But with constant-factor growth the total across all inserts is linear — the sum `1 + 2 + 4 + ... + n` is under `2n` — so growth adds a constant factor to the whole load, not a new asymptotic term. Worth knowing, not the first thing that bites. ## What actually breaks: the cost model Start with arithmetic, because this is the number an interviewer wants to see you produce unprompted. A billion entries with, say, 32 bytes per stored entry is 32 GB before any table overhead; add empty slots implied by the load factor and per-entry metadata and the real figure is meaningfully larger. Two consequences follow. First, if that exceeds available memory, the analysis is over — the process either fails or starts paging, and a paging hash table is thousands of times slower than the one you analysed, because its access pattern is the worst possible one for a pager. Second, even when it fits, locality collapses. A hash function's entire purpose is to scatter keys, which means consecutive lookups touch unrelated addresses. With a table far larger than the last-level cache, essentially every probe is a cache miss; probing several slots in a run means several independent misses. The operation is still O(1) — one or two probes, exactly as analysed — but each probe now costs on the order of a hundred nanoseconds instead of a few. Nothing about the complexity claim is wrong; it simply was never a statement about time on a memory hierarchy. ## What "expected" quietly assumes about your keys The second-order answer, and the one that separates a strong candidate: expected O(1) assumes the stored keys scatter across buckets, and that is about **distinct** keys. A dedupe workload has a specific and helpful property — heavy duplication of a few values. Duplicates do not create collisions or entries; they resolve to the existing entry. So a billion records with only fifty million distinct keys is a fifty-million-entry problem, and the first measurement worth taking is the distinct cardinality, not the record count. Getting that backwards, and sizing for a billion entries when the data holds a twentieth of that, is the most expensive mistake available here. ## The engineering answer Budget before you benchmark. Estimate the distinct cardinality, multiply by realistic bytes per entry including overhead, and compare against the ceiling you are allowed. If it fits with headroom, the expected-O(1) structure is the right call and you should expect memory-bound rather than compute-bound behaviour, judging progress in records per second against memory bandwidth rather than against instruction counts. If it does not fit, the structure is not the problem and no tuning of it will help: partition the key space by a hash prefix into passes that each fit, dedupe within each pass, and pay one extra sequential read of the input — trading a random-access problem you cannot afford for a sequential one you can. If exactness is negotiable, a probabilistic membership structure cuts the memory by an order of magnitude at the price of a bounded false-positive rate, and whether that is acceptable is a product decision rather than an algorithmic one. The throughline: the asymptotics were never wrong. The claim held; the machine model underneath it stopped being a good description of the machine.
- Why do collision pileups not get worse as the entry count grows?Because expected chain length is the load factor, n divided by the bucket count, and a growth policy keeps that ratio roughly fixed as n rises. A billion-entry table therefore has the same expected probe count as a million-entry one. Only the longest chain grows, and only as log n over log log n — from single digits to low double digits across three orders of magnitude.
- The input has a billion records but far fewer distinct keys. How does that change your sizing?Completely. A hash set stores one entry per distinct key, so duplicates cost a lookup and nothing else — no entry, no collision, no growth. Size the table against measured or estimated distinct cardinality, not record count. Getting this backwards can over-provision memory by an order of magnitude, and estimating cardinality cheaply on a sample is the first measurement to take.
- The set does not fit in the memory you are allowed. What changes?The structure stops being the lever. Partition the key space by a hash prefix into k ranges, run the dedupe once per range so each pass's working set fits, and pay one extra sequential scan of the input per pass. That converts an unaffordable random-access problem into an affordable sequential one. If exact answers are negotiable, a probabilistic membership structure cuts memory sharply at the cost of a bounded false-positive rate.
saying these in an interview costs you the question
- At a billion keys the collisions must be piling up
- Big-O stops being valid above some input size
- Sizing the table by record count instead of distinct keys
- Assuming every memory access costs the same at any scale
- Treating a memory-bound workload as a hash-function tuning problem