"Immutable-js Map Transient" means an Immutable-js Map that uses "withMutations" for extra speed.
I also want to test out ClojureScript's data structures, but I haven't gotten around to it yet.
----
As I said earlier, AVL trees are fast. They tend to be 0-3x slower than JavaScript objects.
Stop and think about that for a moment: JavaScript objects are mutable, they're written in C++, they're heavily optimized with various tricks, and they're probably implemented as hash tables.
Meanwhile, AVL trees are immutable, do not use any kind of optimization tricks (the algorithms are very simple and straightforward), and they are implemented in vanilla JavaScript, in less than 400 lines of code.
JavaScript engines are fast. So fast that immutable AVL trees are completely viable. I would not hesitate to replace all my programs with them. This is quite amazing.
In addition, notice that according to those benchmark numbers, you can create 20 empty AVL trees, then insert 100 keys into each AVL tree (for a total of 2,000 insert operations)... once per millisecond. In this case, worrying about the performance cost of immutability is a huge premature optimization.
(I'm not directing this at anybody in particular, in fact I am the kind of person that does premature optimization, so these numbers help with my own personal fears about performance.)
----
I'll test the performance of unsorted arrays soon and post the results here.
How disappointing. Mori[1] (which uses ClojureScript's data structures) was either the same as Immutable-js, or significantly worse. In the end, AVL trees win by a large margin for small to medium dictionaries, while Immutable-js performs better for large (> 100 keys) dictionaries.
Get/insert/remove 1 key:
Immutable AVL Tree x 37,909,434 ops/sec ±0.37% (101 runs sampled)
Immutable-js Map x 19,492,874 ops/sec ±0.15% (101 runs sampled)
Mori Hash Map x 2,306,565 ops/sec ±0.74% (96 runs sampled)
Mori Sorted Map x 13,424,409 ops/sec ±0.51% (97 runs sampled)
Immutable AVL Tree x 6,257,569 ops/sec ±0.43% (99 runs sampled)
Immutable-js Map x 2,111,085 ops/sec ±1.07% (91 runs sampled)
Mori Hash Map x 1,553,193 ops/sec ±0.77% (93 runs sampled)
Mori Sorted Map x 3,785,671 ops/sec ±0.43% (96 runs sampled)
Immutable AVL Tree x 3,426,260 ops/sec ±1.38% (97 runs sampled)
Immutable-js Map x 1,415,893 ops/sec ±0.41% (96 runs sampled)
Mori Hash Map x 699,113 ops/sec ±0.40% (98 runs sampled)
Mori Sorted Map x 1,550,116 ops/sec ±1.54% (100 runs sampled)
Get/insert/remove 10 keys:
Immutable AVL Tree x 21,954,005 ops/sec ±0.81% (98 runs sampled)
Immutable-js Map x 17,236,706 ops/sec ±1.02% (99 runs sampled)
Mori Hash Map x 2,474,120 ops/sec ±0.77% (95 runs sampled)
Mori Sorted Map x 911,264 ops/sec ±0.41% (100 runs sampled)
Immutable AVL Tree x 399,700 ops/sec ±0.15% (97 runs sampled)
Immutable-js Map x 218,274 ops/sec ±0.63% (98 runs sampled)
Mori Hash Map x 150,978 ops/sec ±0.74% (96 runs sampled)
Mori Sorted Map x 73,598 ops/sec ±0.68% (98 runs sampled)
Immutable AVL Tree x 135,120 ops/sec ±0.76% (99 runs sampled)
Immutable-js Map x 100,893 ops/sec ±0.20% (97 runs sampled)
Mori Hash Map x 74,750 ops/sec ±10.95% (96 runs sampled)
Mori Sorted Map x 42,696 ops/sec ±0.45% (99 runs sampled)
Get/insert/remove 100 keys:
Immutable AVL Tree x 6,467,149 ops/sec ±0.38% (93 runs sampled)
Immutable-js Map x 14,233,214 ops/sec ±1.05% (96 runs sampled)
Mori Hash Map x 2,513,928 ops/sec ±1.24% (98 runs sampled)
Mori Sorted Map x 384,132 ops/sec ±0.53% (98 runs sampled)
Immutable AVL Tree x 19,760 ops/sec ±0.52% (100 runs sampled)
Immutable-js Map x 12,798 ops/sec ±0.26% (100 runs sampled)
Mori Hash Map x 10,619 ops/sec ±2.59% (93 runs sampled)
Mori Sorted Map x 3,078 ops/sec ±1.49% (98 runs sampled)
Immutable AVL Tree x 6,420 ops/sec ±0.51% (101 runs sampled)
Immutable-js Map x 6,204 ops/sec ±0.20% (99 runs sampled)
Mori Hash Map x 5,394 ops/sec ±0.81% (93 runs sampled)
Mori Sorted Map x 1,757 ops/sec ±0.51% (100 runs sampled)
So, today I learned that cons cells, despite having O(n) behavior, are really fast. They outperform mutable JS arrays at random inserts!
It's only once you get up to ~100 elements that AVL trees start to outperform cons cells. A good optimization would be to use cons cells for small lists, and then automatically switch to AVL trees once the list grows to be a certain size.