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  ```