Sparse Set Container
A container based on a sparse set.
It is useful if you want a container with performance close to Vec but you also want to safely store the indexes to the elements (so that they are not invalidated on removals).
E.g. you have a list of elements in UI that the user can add and remove, but you want to refer to the elements of that list from somewhere else.
Usage
Add this to your Cargo.toml:
[]
= "1.1"
Description
An array-like container based on sparse set implementation that allows O(1) access to elements without hashing and allows cache-friendly iterations.
| Operation | SparseSet | Vec |
|---|---|---|
| push | O(1) | O(1) |
| lookup | O(1) | O(1) |
| size/len | O(1) | O(1) |
| remove | O(n) | O(n) |
| swap_remove | O(1) | O(1) |
For iterating over the elements SparseSet exposes an iterator over an internal Vec with values, which is as efficient as iterating over a Vec directly.
Differences to Vec:
- Instead of using indexes, when adding an element, it returns a lightweight key structure that can be used to access the element later
- The key is not invalidated when elements are removed from the container
- If the pointed-at element was removed, the key will not be pointing to any other elements, even if new elements are inserted
- There is a slight overhead in insertion/lookup/removal operations compared to Vec
- Consumes more memory:
- for each value
4*sizeof(usize)bytes on top of the size of the element itself- (e.g. 32 bytes per element on 64-bit systems)
- per each
2^(sizeof(usize)*8)removals the memory consumption will also grow by2*sizeof(usize)- (e.g. 16 bytes per 18446744073709551616 elements removed on 64-bit systems)
- for each value
- Many Vec operations are not supported (create an issue on github if you want to request one)
Examples
extern crate sparse_set_container;
use SparseSet;
Benchmarks
The values captured to illustrate the difference between this SparseSet container implementation, Vec, and standard HashMap:
| Benchmark | SparseSet<String> |
Vec<String> |
HashMap<i32, String> |
|---|---|---|---|
| Create empty | 0 ns ±0 | 0 ns ±0 | 1 ns ±0 |
| Create with capacity | 17 ns ±0 | 16 ns ±0 | 33 ns ±1 |
| Push 100 elements | 3,365 ns ±32 | 2,459 ns ±30 | 5,432 ns ±137 |
| With capacity push 100 | 3,253 ns ±32 | 3,099 ns ±29 | 4,368 ns ±32 |
| Lookup 100 elements | 88 ns ±0 | 41 ns ±4 | 464 ns ±3 |
| Iterate over 100 elements | 30 ns ±0 | 30 ns ±1 | 41 ns ±1 |
| Clone with 100 elements | 2,144 ns ±13 | 2,094 ns ±6 | 1,468 ns ±35 |
| Clone 100 and remove 10 | 3,062 ns ±122 | 2,322 ns ±117 | 1,693 ns ±127 |
| Clone 100 and swap_remove 10 | 2,434 ns ±93 | 2,171 ns ±102 | N/A |
To run the benchmark on your machine, execute cargo run --example bench --release
Or to build this table you can run python tools/collect_benchmark_table.py and then find the results in bench_table.md
License
Licensed under the MIT license: http://opensource.org/licenses/MIT