Benchmarks
Every number below came from a real run, timed with Python's own timeit, on the machine described here. Where a figure goes beyond what was actually measured (capacity planning past what fits on one laptop) it is marked as calculated or extrapolated, not measured.
Complexity, in Big O
Every measurement further down is a real-world reading of the same underlying complexity. Here it is in the form you can reason about without a stopwatch.
Reading the notation: k and m are fixed per filter, so O(k) and O(m) are a flat cost, not one that grows with n. Nothing here is logarithmic or quadratic, staying flat is the whole point.
Throughput
Every figure below is at 100,000 items and a 0.1% target error rate, unless noted otherwise. Hover a bar for the exact number.
The gap between the two charts is the entire reason ManagedBloomFilter and ShardedBloomFilter exist. In-memory operations run in low single-digit microseconds. Writing a checkpoint means serializing and fsyncing real bytes to disk, three orders of magnitude slower, which is exactly why you control how often it happens rather than doing it on every insert.
Scaling with capacity
Bit array size grows linearly with expected item count. Past a few hundred million items, these numbers move from measured to calculated, the formula in core.py does not change with scale.
Concurrency, honestly
BloomFilter must not be shared across threads without a lock
self._bits[byte_index] |= 1 << bit_offset
That line compiles to separate load, or, and store bytecodes. A GIL handoff between the read and the write, on the same byte, from two threads, is a genuine lost-update hazard: one thread's bit can be silently overwritten by another's stale copy of the same byte.
A stress test of 16 threads doing 500 inserts each into a shared 2-byte array, forced to collide, produced 0 bit differences against a single-threaded reference across every trial run. That result does not prove safety, it only means this specific run did not happen to trigger the race. Put a lock around writes if more than one thread touches the same filter.
Capacity planner
Pick an expected item count and a target error rate. These numbers come straight from optimal_size and optimal_hash_count, the same functions the library calls internally.
Checkpoint and load figures assume a single file with the OS page cache warm. A cold-cache disk read will be slower than what is shown here.
Full capacity table
All 98 rows behind the planner above, every combination of the 7 capacities and 14 error rates the suite covers.
Click a header to sort. Click any cell to select its row and column, Excel-style, useful for lining up a number with its label while scrolling.
Full operation log
All 194 timed records from the run. Search by operation, scenario, or note, or filter to one operation at a time.
MEASURED figures were timed directly. CALCULATED figures come from exact formulas. EXTRAPOLATED figures project past what was actually run, see the notes column for the caveat.