-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HashMap performance issues #9
Comments
Continuing the discussion from fslaborg/Deedle#52 (comment): Recently I have read a lot about different persistent data structures and thought that Clojure's version of hash array mapped trie is probably the best. Thanks to this implementation in F# I could now test it and I have just benchmarked PersistentHashMap against FSharp.Core.Map. Base on my draft/unchecked/approximate numbers current version of PHM is slower and consumes significantly more memory than the simple AVL tree. I would expect PHM to be faster given that its depth is much less than of binary tree, and memory usage should be less given that there are less references from nodes. Your Vector implementation based on similar algo is very fast, so the current results are somewhat unexpected. The benchmark is just simple iteration of 1 Mn inserts and lookups of <string, int64> and <int64, int64> (https://gist.github.com/buybackoff/ee57b2fe5919448b9b2b). The results from my machine below. Interesting that switching from string to int64 keys doubles AVL tree performance, but the numbers for PHM increase just slightly. That could indicate that there is some constant overhead not related to data in the current implementation of PHM. PersistentHashMap
int64,int64:
MapTree from FSharp.Core Map internal implementation
int64,int64:
For 10Mn (int64,int64) PHM runs much longer than 10x of 1Mn time (killed the test, didn't wait). Memory is raising and falling in TaskManager - obviously GC does a lot of work. For TreeMap performance drops by c.30% (near expected log(1M)/log(10M)). P.S. As a separate note, I wonder why there are at least two places with different implementations of persistent collections: this repo (FShapx), and ExtCore (https://github.com/jack-pappas/ExtCore/blob/master/ExtCore/Collections.HashMap.fs)? In the last one a persistent Map is already implemented using Patricia trie vs array-mapped here. Some collection duplicate each other (e.g. IntMap), are they synchronized? |
I ported the HashMap from Clojure and honestly I don't know why it is so slow. I think the port is correct in functionality, but maybe there should be used other node types in .NET |
From profiler: Function Name Exclusive Samples % 50+% of inserts work is done in
For reads only tail call is the greatest overhead. |
Can you fix it? |
Not sure there is an easy "fix": array copying is by design, recursive methods are on interfaces with two implementations that call each other - couldn't easily change this to a loop. I do not see any particular bug here. I am not even sure that this is slow in absolute terms. My expectations were based on Vector performance and I expected this map to perform closer to Vector at least on reads (and at least in int64-int64 case). |
Renaming to reflect this issue has turned into a performance issue. No idea if this is still current,. #110 should be used to shed light on this. |
Same benchmark for PersistentHashMap as in dotnet/fsharp#5360
Asymptotically (BigO) Log32 is 5x faster than Log2 for AVL. Constant factor could be optimized to some extent (e.g. popcount via intrinsics, virtual calls, etc.). But memory usage is still too big vs intuition. In the above table memory is measured before forced GC. If I force GC from inside the benchmark the results are:
Still memory is 3x more than AVL. What is theoretical memory overhead for an ideal HAMT? |
Hey, I recently started implementing a persistent HashMap for F# and did some benchmarks (incuding the FSharpX PersistenHashMap) in my current sketch-repo https://github.com/krauthaufen/ImmutableHashCollections I think the main reason the FSharpX implementation isn't that fast is boxing/unboxing (due to object arrays) and the memory issue might be caused by capturing the creating-thread for each cell. Also see discussion at fsprojects/FSharp.Data.Adaptive#17 |
The text was updated successfully, but these errors were encountered: