mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-22 08:17:40 +03:00
Swap Dict implementation to ankerl dense unordered
ankerl::dense_unordered is a very fast hash map that is built to be an index map. This enables extra optimizations compared to just wrapping a regular hash map. As such, I think this map is very well suited for our index map impl in Roc. I also think this dictionary implementation is simpler overall. On top of that, this removes the need for SIMD instructions for peak performance. Benchmarks of the C++ version and other C++ hash maps are here: https://martin.ankerl.com/2022/08/27/hashmap-bench-01/ Though this has clear bias of being written by the author of ankerl::dense_unordered, the results all look correct and the benchmarks thorough.
This commit is contained in:
parent
eadd0e82ce
commit
51ec4311b5
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,8 @@ interface Set
|
||||
exposes [
|
||||
Set,
|
||||
empty,
|
||||
withCapacity,
|
||||
reserve,
|
||||
single,
|
||||
walk,
|
||||
walkUntil,
|
||||
@ -45,7 +47,7 @@ Set k := Dict.Dict k {} where k implements Hash & Eq
|
||||
},
|
||||
]
|
||||
|
||||
isEq : Set k, Set k -> Bool where k implements Hash & Eq
|
||||
isEq : Set k, Set k -> Bool
|
||||
isEq = \xs, ys ->
|
||||
if len xs != len ys then
|
||||
Bool.false
|
||||
@ -56,7 +58,7 @@ isEq = \xs, ys ->
|
||||
else
|
||||
Break Bool.false
|
||||
|
||||
hashSet : hasher, Set k -> hasher where k implements Hash & Eq, hasher implements Hasher
|
||||
hashSet : hasher, Set k -> hasher where hasher implements Hasher
|
||||
hashSet = \hasher, @Set inner -> Hash.hash hasher inner
|
||||
|
||||
toInspectorSet : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter
|
||||
@ -74,13 +76,18 @@ toInspectorSet = \set ->
|
||||
empty : {} -> Set *
|
||||
empty = \{} -> @Set (Dict.empty {})
|
||||
|
||||
## Return a dictionary with space allocated for a number of entries. This
|
||||
## Return a set with space allocated for a number of entries. This
|
||||
## may provide a performance optimization if you know how many entries will be
|
||||
## inserted.
|
||||
withCapacity : Nat -> Set *
|
||||
withCapacity = \cap ->
|
||||
@Set (Dict.withCapacity cap)
|
||||
|
||||
# Enlarge the set for at least capacity additional elements
|
||||
reserve : Set k, Nat -> Set k
|
||||
reserve = \@Set dict, requested ->
|
||||
@Set (Dict.reserve dict requested)
|
||||
|
||||
## Creates a new `Set` with a single value.
|
||||
## ```
|
||||
## singleItemSet = Set.single "Apple"
|
||||
@ -88,7 +95,7 @@ withCapacity = \cap ->
|
||||
##
|
||||
## expect countValues == 1
|
||||
## ```
|
||||
single : k -> Set k where k implements Hash & Eq
|
||||
single : k -> Set k
|
||||
single = \key ->
|
||||
Dict.single key {} |> @Set
|
||||
|
||||
@ -104,7 +111,7 @@ single = \key ->
|
||||
##
|
||||
## expect countValues == 3
|
||||
## ```
|
||||
insert : Set k, k -> Set k where k implements Hash & Eq
|
||||
insert : Set k, k -> Set k
|
||||
insert = \@Set dict, key ->
|
||||
Dict.insert dict key {} |> @Set
|
||||
|
||||
@ -189,7 +196,7 @@ expect
|
||||
## expect has10 == Bool.false
|
||||
## expect has20 == Bool.true
|
||||
## ```
|
||||
remove : Set k, k -> Set k where k implements Hash & Eq
|
||||
remove : Set k, k -> Set k
|
||||
remove = \@Set dict, key ->
|
||||
Dict.remove dict key |> @Set
|
||||
|
||||
@ -208,7 +215,7 @@ remove = \@Set dict, key ->
|
||||
## expect hasApple == Bool.true
|
||||
## expect hasBanana == Bool.false
|
||||
## ```
|
||||
contains : Set k, k -> Bool where k implements Hash & Eq
|
||||
contains : Set k, k -> Bool
|
||||
contains = \@Set dict, key ->
|
||||
Dict.contains dict key
|
||||
|
||||
@ -221,7 +228,7 @@ contains = \@Set dict, key ->
|
||||
##
|
||||
## expect Set.toList numbers == values
|
||||
## ```
|
||||
toList : Set k -> List k where k implements Hash & Eq
|
||||
toList : Set k -> List k
|
||||
toList = \@Set dict ->
|
||||
Dict.keys dict
|
||||
|
||||
@ -235,7 +242,7 @@ toList = \@Set dict ->
|
||||
##
|
||||
## expect Set.fromList [Pear, Apple, Banana] == values
|
||||
## ```
|
||||
fromList : List k -> Set k where k implements Hash & Eq
|
||||
fromList : List k -> Set k
|
||||
fromList = \list ->
|
||||
list
|
||||
|> List.map \k -> (k, {})
|
||||
@ -252,7 +259,7 @@ fromList = \list ->
|
||||
##
|
||||
## expect Set.union set1 set2 == Set.fromList [Left, Right]
|
||||
## ```
|
||||
union : Set k, Set k -> Set k where k implements Hash & Eq
|
||||
union : Set k, Set k -> Set k
|
||||
union = \@Set dict1, @Set dict2 ->
|
||||
Dict.insertAll dict1 dict2 |> @Set
|
||||
|
||||
@ -265,7 +272,7 @@ union = \@Set dict1, @Set dict2 ->
|
||||
##
|
||||
## expect Set.intersection set1 set2 == Set.single Left
|
||||
## ```
|
||||
intersection : Set k, Set k -> Set k where k implements Hash & Eq
|
||||
intersection : Set k, Set k -> Set k
|
||||
intersection = \@Set dict1, @Set dict2 ->
|
||||
Dict.keepShared dict1 dict2 |> @Set
|
||||
|
||||
@ -279,7 +286,7 @@ intersection = \@Set dict1, @Set dict2 ->
|
||||
##
|
||||
## expect Set.difference first second == Set.fromList [Up, Down]
|
||||
## ```
|
||||
difference : Set k, Set k -> Set k where k implements Hash & Eq
|
||||
difference : Set k, Set k -> Set k
|
||||
difference = \@Set dict1, @Set dict2 ->
|
||||
Dict.removeAll dict1 dict2 |> @Set
|
||||
|
||||
@ -302,14 +309,14 @@ difference = \@Set dict1, @Set dict2 ->
|
||||
##
|
||||
## expect result == 2
|
||||
## ```
|
||||
walk : Set k, state, (state, k -> state) -> state where k implements Hash & Eq
|
||||
walk : Set k, state, (state, k -> state) -> state
|
||||
walk = \@Set dict, state, step ->
|
||||
Dict.walk dict state (\s, k, _ -> step s k)
|
||||
|
||||
## Convert each value in the set to something new, by calling a conversion
|
||||
## function on each of them which receives the old value. Then return a
|
||||
## new set containing the converted values.
|
||||
map : Set a, (a -> b) -> Set b where a implements Hash & Eq, b implements Hash & Eq
|
||||
map : Set a, (a -> b) -> Set b
|
||||
map = \set, transform ->
|
||||
init = withCapacity (capacity set)
|
||||
|
||||
@ -321,7 +328,7 @@ map = \set, transform ->
|
||||
## (using [Set.union]) into one set.
|
||||
##
|
||||
## You may know a similar function named `concatMap` in other languages.
|
||||
joinMap : Set a, (a -> Set b) -> Set b where a implements Hash & Eq, b implements Hash & Eq
|
||||
joinMap : Set a, (a -> Set b) -> Set b
|
||||
joinMap = \set, transform ->
|
||||
init = withCapacity (capacity set) # Might be a pessimization
|
||||
|
||||
@ -343,7 +350,7 @@ joinMap = \set, transform ->
|
||||
##
|
||||
## expect result == FoundTheAnswer
|
||||
## ```
|
||||
walkUntil : Set k, state, (state, k -> [Continue state, Break state]) -> state where k implements Hash & Eq
|
||||
walkUntil : Set k, state, (state, k -> [Continue state, Break state]) -> state
|
||||
walkUntil = \@Set dict, state, step ->
|
||||
Dict.walkUntil dict state (\s, k, _ -> step s k)
|
||||
|
||||
|
@ -1486,6 +1486,7 @@ define_builtins! {
|
||||
26 DICT_JOINMAP: "joinMap"
|
||||
27 DICT_KEEP_IF: "keepIf"
|
||||
28 DICT_DROP_IF: "dropIf"
|
||||
29 DICT_RESERVE: "reserve"
|
||||
}
|
||||
9 SET: "Set" => {
|
||||
0 SET_SET: "Set" exposed_type=true // the Set.Set type alias
|
||||
@ -1510,6 +1511,8 @@ define_builtins! {
|
||||
19 SET_JOIN_MAP: "joinMap"
|
||||
20 SET_KEEP_IF: "keepIf"
|
||||
21 SET_DROP_IF: "dropIf"
|
||||
22 SET_WITH_CAPACITY: "withCapacity"
|
||||
23 SET_RESERVE: "reserve"
|
||||
}
|
||||
10 BOX: "Box" => {
|
||||
0 BOX_BOX_TYPE: "Box" exposed_apply_type=true // the Box.Box opaque type
|
||||
|
Loading…
Reference in New Issue
Block a user