src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/persistent/README.md (about) 1 # Persistent data structure in Go 2 3 This is a Go clone of Clojure's persistent data structures. 4 5 License is 6 [Eclipse Public License 1.0](http://opensource.org/licenses/eclipse-1.0.php) 7 (like Clojure). 8 9 ## Implementation notes 10 11 The list provided here is a singly-linked list and is very trivial to implement. 12 13 The implementation of persistent vector and hash map and based on a series of 14 [excellent](http://blog.higher-order.net/2009/02/01/understanding-clojures-persistentvector-implementation) 15 [blog](http://blog.higher-order.net/2009/09/08/understanding-clojures-persistenthashmap-deftwice) 16 [posts](http://blog.higher-order.net/2010/08/16/assoc-and-clojures-persistenthashmap-part-ii.html) 17 as well as the Clojure source code. Despite the hash map appearing more 18 complicated, the vector is slightly harder to implement due to the "tail array" 19 optimization and some tricky transformation of the tree structure, which is 20 fully replicated here. 21 22 ## Benchmarking results 23 24 ### Vectors 25 26 Compared to native slices, 27 28 - Adding elements is anywhere from 5x to 9x as slow. 29 30 - Read (sequential or random) is about 6x as slow. 31 32 Benchmarked on an MacBook Air (M1, 2020), with Go 1.17.5: 33 34 ``` 35 BenchmarkConjNativeN1-8 1779234 673.3 ns/op 36 BenchmarkConjNativeN2-8 948654 1220 ns/op 37 BenchmarkConjNativeN3-8 61242 20138 ns/op 38 BenchmarkConjNativeN4-8 1222 968176 ns/op 39 BenchmarkConjPersistentN1-8 264488 4462 ns/op 6.63x 40 BenchmarkConjPersistentN2-8 119526 9885 ns/op 8.10x 41 BenchmarkConjPersistentN3-8 6760 173995 ns/op 8.64x 42 BenchmarkConjPersistentN4-8 212 5576977 ns/op 5.76x 43 BenchmarkIndexSeqNativeN4-8 32031 37344 ns/op 44 BenchmarkIndexSeqPersistentN4-8 6145 192151 ns/op 5.15x 45 BenchmarkIndexRandNative-8 31366 38016 ns/op 46 BenchmarkIndexRandPersistent-8 5434 216284 ns/op 5.69x 47 BenchmarkEqualNative-8 110090 10738 ns/op 48 BenchmarkEqualPersistent-8 2121 557334 ns/op 51.90x 49 ``` 50 51 ### Hash map 52 53 Compared to native maps, adding elements is about 3-6x slow. Difference is more 54 pronunced when keys are sequential integers, but that workload is very rare in 55 the real world. 56 57 Benchmarked on an MacBook Air (M1, 2020), with Go 1.17.5: 58 59 ``` 60 goos: darwin 61 goarch: arm64 62 pkg: src.elv.sh/pkg/persistent/hashmap 63 BenchmarkSequentialConjNative1-8 620540 1900 ns/op 64 BenchmarkSequentialConjNative2-8 22918 52209 ns/op 65 BenchmarkSequentialConjNative3-8 567 2115886 ns/op 66 BenchmarkSequentialConjPersistent1-8 169776 7026 ns/op 3.70x 67 BenchmarkSequentialConjPersistent2-8 3374 354031 ns/op 6.78x 68 BenchmarkSequentialConjPersistent3-8 51 23091870 ns/op 10.91x 69 BenchmarkRandomStringsConjNative1-8 379147 3155 ns/op 70 BenchmarkRandomStringsConjNative2-8 10000 117332 ns/op 71 BenchmarkRandomStringsConjNative3-8 292 4034937 ns/op 72 BenchmarkRandomStringsConjPersistent1-8 96504 12207 ns/op 3.87x 73 BenchmarkRandomStringsConjPersistent2-8 1910 615644 ns/op 5.25x 74 BenchmarkRandomStringsConjPersistent3-8 33 31928604 ns/op 7.91x 75 ```