4 ms·
> by a flat array for small n (hundreds, low thousands) Some of us are working in, say, Python. A flat array can outperform at small n, yes, but people overest
by deathanatos 7y ago
> by a flat array for small n (hundreds, low thousands)
Some of us are working in, say, Python. A flat array can outperform at small n, yes, but people overestimate where the tradeoff point is. It's at <5 items:
# A list of [0, 1, 2, 3, 4]
In [10]: linear = list(range(5))
# A hash set, same thing.
In [11]: hashing = set(range(5))
# 44ns / linear search
In [12]: %timeit 3 in linear
44.2 ns ± 0.412 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
# 25ns / hash search!
In [13]: %timeit 3 in hashing
25 ns ± 0.6 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
The hash set outperforms the linear search by nearly 2x, on a list of size 5! (The performance is similar for other common types that end up in hashes, like strings.)
"It's Python!", you say. "Too much chasing of pointers to PyObjects destroy the cache!" And yes, they do; but many people are working in high-level languages like Python or Ruby.
But, for those that aren't, if we repeat the above exercise in Rust, yes the tradeoff will move up, but only to ~60 items, not hundreds or low thousands:
test tests::bench_hash_int ... bench: 14 ns/iter (+/- 1)
test tests::bench_linear_int ... bench: 19 ns/iter (+/- 3)
If you're thinking that somehow accessing the middle item each time bestows an unfair advantage to the hash table, randomizing the desired item doesn't help, either:
test tests::bench_rng_hash_int ... bench: 19 ns/iter (+/- 2)
test tests::bench_rng_linear_int ... bench: 24 ns/iter (+/- 2)
And looking for an item not in the list is definitely not favorable to the linear search. (It's the worst case.)
In my experience, it's almost always easiest to pay mild attention to big O concerns, and just use the appropriate data structure for the problem at hand. Cache effects mattering is either rare (you're writing a RESTful microserving to push cat pictures, a cache isn't going to matter once we hit this mobile devices 20 second network latency!) or highly context dependent (your line of work is always low-level, and these crop up more often, and you're consequently on the lookout for it; I don't think this applies to most of us, however).
The code used, in case you wish to find fault with it: https://github.com/thanatos/hash-vs-linear https://github.com/thanatos/hash-vs-linear
- XelNika 7y agoYou only benchmarked the search itself, but in the real world it might also take time to set up the data. You can't really pinpoint a tradeoff point without knowing how many times a data structure will be used. I ran your Python test on my machine and the hash set was faster in every case: 10x faster at size 50, 2x faster at size 5, 1.3x faster at size 3.
- deathanatos 7y agoThat's not a bad point, and perhaps I should test creation times too. (I wanted to ignore that s.t. the test isolated a single thing: searching / wasn't mixing two things together.) Both hashsets and vectors are O(n) in setup, and I'd mostly expect their real-world performance to be very similar: hash tables tend to use contiguous arrays for storage and vectors do by definition, both tend to overallocate (hash tables to prevent collisions, vectors to amortize appends to O(1)), so I'd expect the performance in the real-world to be similar. Vectors tend to insert in-order, though, whereas the insertions in hashsets would bounce around the table, so that might make it more interesting.
- XelNika 7y agoYes, it's always best to reduce the number of variables in benchmarking, but the setup makes a big difference when we are talking about tiny data sets. I don't know how to test it properly, but I tried looping over both the initialisation and a single search, and the hash sets were much slower, so much so that hash sets trailed lists by microseconds at size 10000. In retrospect, making those data structures unsurprisingly dominated running time and the test kind of lost all meaning. It was clear, however, that it wouldn't take many searches for the hash sets to win, search times for lists were going through the roof.