github.com/bytedance/gopkg@v0.0.0-20240514070511-01b2cbcf35e1/collection/skipset/readme.md (about)

     1  <p align="center">
     2    <img src="https://raw.githubusercontent.com/zhangyunhao116/public-data/master/skipset-logo2.png"/>
     3  </p>
     4  
     5  ## Introduction
     6  
     7  skipset is a high-performance concurrent set based on skip list. In typical pattern(100000 operations, 90%CONTAINS 9%ADD 1%REMOVE), the skipset up to 3x ~ 15x faster than the built-in sync.Map.
     8  
     9  The main idea behind the skipset is [A Simple Optimistic Skiplist Algorithm](<https://people.csail.mit.edu/shanir/publications/LazySkipList.pdf>).
    10  
    11  Different from the sync.Map, the items in the skipset are always sorted, and the `Contains` and `Range` operations are wait-free (A goroutine is guaranteed to complete an operation as long as it keeps taking steps, regardless of the activity of other goroutines).
    12  
    13  
    14  ## Features
    15  
    16  - Concurrent safe API with high-performance.
    17  - Wait-free Contains and Range operations.
    18  - Sorted items.
    19  
    20  
    21  
    22  ## When should you use skipset
    23  
    24  In these situations, `skipset` is better
    25  
    26  - **Sorted elements is needed**.
    27  - **Concurrent calls multiple operations**. such as use both `Contains` and `Add` at the same time.
    28  - **Memory intensive**. The skipset save at least 50% memory in the benchmark.
    29  
    30  In these situations, `sync.Map` is better
    31  
    32  - Only one goroutine access the set for most of the time, such as insert a batch of elements and then use only `Contains` (use built-in map is even better).
    33  
    34  
    35  
    36  ## QuickStart
    37  
    38  ```go
    39  package main
    40  
    41  import (
    42  	"fmt"
    43  
    44  	"github.com/bytedance/gopkg/collection/skipset"
    45  )
    46  
    47  func main() {
    48  	l := NewInt()
    49  
    50  	for _, v := range []int{10, 12, 15} {
    51  		if l.Add(v) {
    52  			fmt.Println("skipset add", v)
    53  		}
    54  	}
    55  
    56  	if l.Contains(10) {
    57  		fmt.Println("skipset contains 10")
    58  	}
    59  
    60  	l.Range(func(value int) bool {
    61  		fmt.Println("skipset range found ", value)
    62  		return true
    63  	})
    64  
    65  	l.Remove(15)
    66  	fmt.Printf("skipset contains %d items\r\n", l.Len())
    67  }
    68  
    69  ```
    70  
    71  
    72  
    73  ## Benchmark
    74  
    75  Go version: go1.16.2 linux/amd64
    76  
    77  CPU: AMD 3700x(8C16T), running at 3.6GHz
    78  
    79  OS: ubuntu 18.04
    80  
    81  MEMORY: 16G x 2 (3200MHz)
    82  
    83  ![benchmark](https://raw.githubusercontent.com/zhangyunhao116/public-data/master/skipset-benchmark.png)
    84  
    85  ```shell
    86  $ go test -run=NOTEST -bench=. -benchtime=100000x -benchmem -count=20 -timeout=60m  > x.txt
    87  $ benchstat x.txt
    88  ```
    89  
    90  ```
    91  name                                              time/op
    92  Int64/Add/skipset-16                              86.6ns ±11%
    93  Int64/Add/sync.Map-16                              674ns ± 6%
    94  Int64/Contains50Hits/skipset-16                   9.85ns ±12%
    95  Int64/Contains50Hits/sync.Map-16                  14.7ns ±30%
    96  Int64/30Add70Contains/skipset-16                  38.8ns ±18%
    97  Int64/30Add70Contains/sync.Map-16                  586ns ± 5%
    98  Int64/1Remove9Add90Contains/skipset-16            24.9ns ±17%
    99  Int64/1Remove9Add90Contains/sync.Map-16            493ns ± 5%
   100  Int64/1Range9Remove90Add900Contains/skipset-16    25.9ns ±16%
   101  Int64/1Range9Remove90Add900Contains/sync.Map-16   1.00µs ±12%
   102  String/Add/skipset-16                              130ns ±14%
   103  String/Add/sync.Map-16                             878ns ± 4%
   104  String/Contains50Hits/skipset-16                  18.3ns ± 9%
   105  String/Contains50Hits/sync.Map-16                 19.2ns ±18%
   106  String/30Add70Contains/skipset-16                 61.0ns ±15%
   107  String/30Add70Contains/sync.Map-16                 756ns ± 7%
   108  String/1Remove9Add90Contains/skipset-16           31.3ns ±13%
   109  String/1Remove9Add90Contains/sync.Map-16           614ns ± 6%
   110  String/1Range9Remove90Add900Contains/skipset-16   36.2ns ±18%
   111  String/1Range9Remove90Add900Contains/sync.Map-16  1.20µs ±17%
   112  
   113  name                                              alloc/op
   114  Int64/Add/skipset-16                               65.0B ± 0%
   115  Int64/Add/sync.Map-16                               128B ± 1%
   116  Int64/Contains50Hits/skipset-16                    0.00B     
   117  Int64/Contains50Hits/sync.Map-16                   0.00B     
   118  Int64/30Add70Contains/skipset-16                   19.0B ± 0%
   119  Int64/30Add70Contains/sync.Map-16                  77.7B ±16%
   120  Int64/1Remove9Add90Contains/skipset-16             5.00B ± 0%
   121  Int64/1Remove9Add90Contains/sync.Map-16            57.5B ± 4%
   122  Int64/1Range9Remove90Add900Contains/skipset-16     5.00B ± 0%
   123  Int64/1Range9Remove90Add900Contains/sync.Map-16     255B ±22%
   124  String/Add/skipset-16                              97.0B ± 0%
   125  String/Add/sync.Map-16                              152B ± 0%
   126  String/Contains50Hits/skipset-16                   15.0B ± 0%
   127  String/Contains50Hits/sync.Map-16                  15.0B ± 0%
   128  String/30Add70Contains/skipset-16                  40.0B ± 0%
   129  String/30Add70Contains/sync.Map-16                 98.2B ±11%
   130  String/1Remove9Add90Contains/skipset-16            23.0B ± 0%
   131  String/1Remove9Add90Contains/sync.Map-16           73.9B ± 4%
   132  String/1Range9Remove90Add900Contains/skipset-16    23.0B ± 0%
   133  String/1Range9Remove90Add900Contains/sync.Map-16    261B ±18%
   134  
   135  name                                              allocs/op
   136  Int64/Add/skipset-16                                1.00 ± 0%
   137  Int64/Add/sync.Map-16                               4.00 ± 0%
   138  Int64/Contains50Hits/skipset-16                     0.00     
   139  Int64/Contains50Hits/sync.Map-16                    0.00     
   140  Int64/30Add70Contains/skipset-16                    0.00     
   141  Int64/30Add70Contains/sync.Map-16                   1.00 ± 0%
   142  Int64/1Remove9Add90Contains/skipset-16              0.00     
   143  Int64/1Remove9Add90Contains/sync.Map-16             0.00     
   144  Int64/1Range9Remove90Add900Contains/skipset-16      0.00     
   145  Int64/1Range9Remove90Add900Contains/sync.Map-16     0.00     
   146  String/Add/skipset-16                               2.00 ± 0%
   147  String/Add/sync.Map-16                              5.00 ± 0%
   148  String/Contains50Hits/skipset-16                    1.00 ± 0%
   149  String/Contains50Hits/sync.Map-16                   1.00 ± 0%
   150  String/30Add70Contains/skipset-16                   1.00 ± 0%
   151  String/30Add70Contains/sync.Map-16                  2.00 ± 0%
   152  String/1Remove9Add90Contains/skipset-16             1.00 ± 0%
   153  String/1Remove9Add90Contains/sync.Map-16            1.00 ± 0%
   154  String/1Range9Remove90Add900Contains/skipset-16     1.00 ± 0%
   155  String/1Range9Remove90Add900Contains/sync.Map-16    1.00 ± 0%
   156  ```