{"id":13713456,"url":"https://github.com/sean-public/fast-skiplist","last_synced_at":"2026-01-17T02:23:20.908Z","repository":{"id":37334817,"uuid":"84820954","full_name":"sean-public/fast-skiplist","owner":"sean-public","description":"A fast, threadsafe skip list in Go","archived":false,"fork":false,"pushed_at":"2021-09-28T07:11:58.000Z","size":26,"stargazers_count":266,"open_issues_count":0,"forks_count":52,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-05-06T23:38:33.233Z","etag":null,"topics":["data-structures","go","golang","skiplist"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sean-public.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-13T11:53:40.000Z","updated_at":"2025-04-28T18:15:04.000Z","dependencies_parsed_at":"2022-08-18T02:10:27.429Z","dependency_job_id":null,"html_url":"https://github.com/sean-public/fast-skiplist","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sean-public/fast-skiplist","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sean-public%2Ffast-skiplist","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sean-public%2Ffast-skiplist/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sean-public%2Ffast-skiplist/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sean-public%2Ffast-skiplist/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sean-public","download_url":"https://codeload.github.com/sean-public/fast-skiplist/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sean-public%2Ffast-skiplist/sbom","scorecard":{"id":807663,"data":{"date":"2025-08-11","repo":{"name":"github.com/sean-public/fast-skiplist","commit":"d7f7945b944e5e211d906cbc4b08a07153d66ecb"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":1,"reason":"Found 4/24 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 7 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T12:13:39.444Z","repository_id":37334817,"created_at":"2025-08-23T12:13:39.444Z","updated_at":"2025-08-23T12:13:39.444Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28492256,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T00:50:05.742Z","status":"online","status_checked_at":"2026-01-17T02:00:07.808Z","response_time":85,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-structures","go","golang","skiplist"],"created_at":"2024-08-02T23:01:36.818Z","updated_at":"2026-01-17T02:23:20.870Z","avatar_url":"https://github.com/sean-public.png","language":"Go","funding_links":[],"categories":["Go","Repositories"],"sub_categories":[],"readme":"##  fast-skiplist\n\n\n\n### Purpose\n\nAs the basic building block of an in-memory data structure store, I needed an implementation of skip lists in Go. It needed to be easy to use and thread-safe while maintaining the properties of a classic skip list.\n\nThere are several skip list implementations in Go. However, they all are implemented in slightly different ways with sparse optimizations and occasional shortcomings. **Please see the [skiplist-survey](https://github.com/sean-public/skiplist-survey) repo for a comparison of Go skip list implementations (including benchmarks).**\n\nThe purpose of this repo is to offer a new, fast implementation with an easy-to-use interface that will suit general data storage purposes.\n\n| Operation          | Time Complexity |\n| ------------------ | -------- |\n| Insertion          | O(log N) |\n| Removal            | O(log N) |\n| Check if contains  | O(log N) |\n| Enumerate in order | O(N)     |\n\n\n### Quickstart\n\nTo start using the library right away, just do:\n\n```sh\ngo get github.com/sean-public/fast-skiplist\n```\n\nThere are no external dependencies, so you can start using it right away:\n\n```go\nimport github.com/sean-public/fast-skiplist\n\nlist := skiplist.New()\nlist.Set(123, \"This string data is stored at key 123!\")\nfmt.Println(list.Get(123).value)\nfmt.Println(list.Length)\t// prints 1\nlist.Remove(123)\nfmt.Println(list.Length)\t// prints 0\n```\n\nOf course there are tests, including benchmarks and race condition detection with concurrency:\n\n```\n$ go test -cover\nPASS\ncoverage: 100.0% of statements\nok      github.com/sean-public/fast-skiplist    0.006s\n\n$ go test -race\nStructure sizes: SkipList is 136, Element is 48 bytes\nPASS\nok  \tgithub.com/sean-public/fast-skiplist\t41.530s\n\n$ go test -bench=.\nStructure sizes: SkipList is 136, Element is 48 bytes\ngoos: darwin\ngoarch: amd64\npkg: github.com/sean-public/fast-skiplist\nBenchmarkIncSet-8   5000000    370 ns/op    13484040.32 MB/s    62 B/op    3 allocs/op\nBenchmarkIncGet-8   10000000   205 ns/op    48592107.58 MB/s    0 B/op     0 allocs/op\nBenchmarkDecSet-8   10000000   281 ns/op    35547886.82 MB/s    62 B/op    3 allocs/op\nBenchmarkDecGet-8   10000000   212 ns/op    47124462.78 MB/s    0 B/op     0 allocs/op\nPASS\nok  \tgithub.com/sean-public/fast-skiplist\t21.709s\n```\n\n\n\n### About fast-skiplist\n\n\u003e \"Perfection is achieved not when there is nothing more to add, but rather when there is nothing more to take away\"    *— Antoine de Saint-Exupery*\n\nIf fast-skiplist is faster than other packages with the same features, it's because it does *less* wherever possible. It locks less, it blocks less, and it traverses less data. Even with these tricks up its sleeve, it has fewer lines of code than most implementations.\n\n###### Calculating the Height of New Nodes\n\nWhen inserting, it calculates \"height\" directly instead of consecutive \"coin tosses\" to add levels. Additionally, it uses a local PRNG source that isn't blocked globally for improved concurrent insert performance.\n\nThe probability of adding new nodes to each level of the structure (it's *height*) is determined by successively \"rolling the dice\" at each level until it doesn't meet a fixed value *P*. The default *P* values for skip lists in the wild range from 0.25 to 0.5. In this implementation, the default is *1/e*, which is optimal for a general-purpose skip list. To find the derivation of this number, see [Analysis of an optimized search algorithm for skip lists](http://www.sciencedirect.com/science/article/pii/030439759400296U) Kirschenhofer et al (1995).\n\nAlmost all other implementations are using common functions in `math/rand`, which will block because querying the PRNG to determine the height of new nodes [waits then acquires a lock via the system-wide random number generator](http://blog.sgmansfield.com/2016/01/the-hidden-dangers-of-default-rand/). We get around this by assigning a new rand source to each skip list instantiated, so each skip list can only ever block itself. This significantly speeds up insert times when you are managing multiple lists with high concurrency.\n\nAdditionally, this implementation always requests just one number from the PRNG. A pre-computed probability table is used to look up what the *height* of the new node will be. This is faster and offers a fixed calculation time compared to successive \"dice rolls\" for each level. The table is computed for each level *L* using the default *P* value of *1/e*: `math.Pow(1.0/math.E, L-1)`. It is consulted during inserts by querying for a random number in range [0.0,1.0) and finding the highest level in the table where the random number is less than or equal to the computed number.\n\nFor example, let's say `math.Float64()` returned `r=0.029` and the table was pre-computed to contain (with a maximum height of 6):\n\n| height | probability |\n| ------ | ----------- |\n| 1      | 1.000000000 |\n| 2      | 0.367879441 |\n| 3      | 0.135335283 |\n| 4      | 0.049787068 |\n| 5      | 0.018315639 |\n| 6      | 0.006737947 |\n\nSo the height for the new node would be 5 because *p5 \u003e r ≥ p6*, or 0.018315639 \u003e 0.029 ≥ 0.006737947.\n\nI believe this fast new node height calculation to be novel and faster than any others with user-defined *P* values. [Ticki, for example, proposes an O(1) calculation](http://ticki.github.io/blog/skip-lists-done-right/) but it is fixed to *P=0.5* and I haven't encountered any other optimizations of this calculation. In local benchmarks, this optimization saves 10-25ns per insert.\n\n###### Better Cooperative Multitasking\n\nWhy not a lock-free implementation? The overhead created is more than the time spent in contention of a locking version under normal loads. Most research on lock-free structures assume manual alloc/free as well and have separate compaction processes running that are unnecessary in Go (particularly with improved GC as of 1.8). The same is true for the newest variation, [the rotating skip list](http://poseidon.it.usyd.edu.au/~gramoli/web/doc/pubs/rotating-skiplist-preprint-2016.pdf), which claims to be the fastest to date for C/C++ and Java because the compared implementations have maintenance threads with increased overhead for memory management.\n\n\n###### Caching and Search Fingers\n\nAs Pugh described in [A Skip List Cookbook](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.17.524), search \"fingers\" can be retained after each lookup operation. When starting the next operation, the finger will point to where the last one occurred and afford the opportunity to pick up the search there instead of starting at the head of the list. This offers *O(log m)* search times, where *m* is the number of elements between the last lookup and the current one (*m* is always less than *n*).\n\nThis implementation of a search finger does not suffer the usual problem of \"climbing\" up in levels when resuming search because it stores pointers to previous nodes for each level independently.\n\n\n\n### Benchmarks\n\nSpeed is a feature! Below is a set of results demonstrating the flat performance (time per operation) as the list grows to millions of elements. Please see the [skiplist-survey](https://github.com/sean-public/skiplist-survey) repo for complete benchmark results from this and other Go skip list implementations. \n\n![benchmark results chart](http://i.imgur.com/VqUbsWr.png)\n\n\n\n### Todo\n\n- Build more complex test cases (specifically to prove correctness during high concurrency).\n- Benchmark memory usage.\n- Add \"span\" to each element to store the distance to the next node on every level. This gives each node a calculable index (ZRANK and associated commands in Redis).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsean-public%2Ffast-skiplist","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsean-public%2Ffast-skiplist","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsean-public%2Ffast-skiplist/lists"}