> It’s mysterious that Rust’s release version with -i ran slightly faster, though.
That's actually not surprising: a large fraction of time is spent in the map lookup, which in Rust is implemented as a B-tree, thus lookup time is (mildly) dependent on the map size. If keys are lowercased before inserting them, the map ends up having fewer elements.
The Nim version uses instead hash tables, whose lookup time is near-constant (that is, excluding memory hierarchy effects).
> That's actually not surprising: a large fraction of time is spent in the map lookup
This was not the case in my benchmarks: the Rust spent 60% of its time doing regex matches, 25% allocating strings and less than 6% manipulating the map.
This seems an odd choice to me (default to B-Tree instead of hash map). I'd expect a sorted map to be a special case, not a general one. Do you happen to know the rationale?
Use a HashMap when:
- You want to associate arbitrary keys with an arbitrary value.
- You want a cache.
- You want a map, with no extra functionality.
Use a BTreeMap when:
- You're interested in what the smallest or largest key-value pair is.
- You want to find the largest or smallest key that is smaller or larger
than something
- You want to be able to get all of the entries in order on-demand.
- You want a sorted map.
I chose it because I wanted to learn Rust by implementing my own BTreeMap struct. I admit it’s not a good choice performance-wise, and made the comparison with Nim less meaningful.
I’ve updated the code and article with HashMap. It runs about 6~7% faster than BTreeMap.
Slightly confusingly, the authors BTreeMap just appears to be a binary tree, while the standard library BTreeMap is a B-tree: http://en.wikipedia.org/wiki/B-tree
You're still comparing two different data structures. Is there a good hash table in Rust? If you use that instead of the B-tree, I would expect it to be at least as fast as Nim.
B-trees are especially bad for string keys, because comparisons are expensive.
EDIT: From Rust docs: "Currently, our implementation simply performs naive linear search. This provides excellent performance on small nodes of elements which are cheap to compare". (emphasis mine)
Good point! I’ve updated the article accordingly. I learned the Entry thing a few months ago, but Rust’s BTreeMap did not support the entry API at that time, so my code did not use it. Then I totally forgot about it when writing this blog...
That's actually not surprising: a large fraction of time is spent in the map lookup, which in Rust is implemented as a B-tree, thus lookup time is (mildly) dependent on the map size. If keys are lowercased before inserting them, the map ends up having fewer elements.
The Nim version uses instead hash tables, whose lookup time is near-constant (that is, excluding memory hierarchy effects).