mirror of
https://github.com/roc-lang/roc.git
synced 2025-11-11 17:13:53 +00: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
3 changed files with 431 additions and 363 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -2,6 +2,8 @@ interface Set
|
||||||
exposes [
|
exposes [
|
||||||
Set,
|
Set,
|
||||||
empty,
|
empty,
|
||||||
|
withCapacity,
|
||||||
|
reserve,
|
||||||
single,
|
single,
|
||||||
walk,
|
walk,
|
||||||
walkUntil,
|
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 ->
|
isEq = \xs, ys ->
|
||||||
if len xs != len ys then
|
if len xs != len ys then
|
||||||
Bool.false
|
Bool.false
|
||||||
|
|
@ -56,7 +58,7 @@ isEq = \xs, ys ->
|
||||||
else
|
else
|
||||||
Break Bool.false
|
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
|
hashSet = \hasher, @Set inner -> Hash.hash hasher inner
|
||||||
|
|
||||||
toInspectorSet : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter
|
toInspectorSet : Set k -> Inspector f where k implements Inspect & Hash & Eq, f implements InspectFormatter
|
||||||
|
|
@ -74,13 +76,18 @@ toInspectorSet = \set ->
|
||||||
empty : {} -> Set *
|
empty : {} -> Set *
|
||||||
empty = \{} -> @Set (Dict.empty {})
|
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
|
## may provide a performance optimization if you know how many entries will be
|
||||||
## inserted.
|
## inserted.
|
||||||
withCapacity : Nat -> Set *
|
withCapacity : Nat -> Set *
|
||||||
withCapacity = \cap ->
|
withCapacity = \cap ->
|
||||||
@Set (Dict.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.
|
## Creates a new `Set` with a single value.
|
||||||
## ```
|
## ```
|
||||||
## singleItemSet = Set.single "Apple"
|
## singleItemSet = Set.single "Apple"
|
||||||
|
|
@ -88,7 +95,7 @@ withCapacity = \cap ->
|
||||||
##
|
##
|
||||||
## expect countValues == 1
|
## expect countValues == 1
|
||||||
## ```
|
## ```
|
||||||
single : k -> Set k where k implements Hash & Eq
|
single : k -> Set k
|
||||||
single = \key ->
|
single = \key ->
|
||||||
Dict.single key {} |> @Set
|
Dict.single key {} |> @Set
|
||||||
|
|
||||||
|
|
@ -104,7 +111,7 @@ single = \key ->
|
||||||
##
|
##
|
||||||
## expect countValues == 3
|
## expect countValues == 3
|
||||||
## ```
|
## ```
|
||||||
insert : Set k, k -> Set k where k implements Hash & Eq
|
insert : Set k, k -> Set k
|
||||||
insert = \@Set dict, key ->
|
insert = \@Set dict, key ->
|
||||||
Dict.insert dict key {} |> @Set
|
Dict.insert dict key {} |> @Set
|
||||||
|
|
||||||
|
|
@ -189,7 +196,7 @@ expect
|
||||||
## expect has10 == Bool.false
|
## expect has10 == Bool.false
|
||||||
## expect has20 == Bool.true
|
## 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 ->
|
remove = \@Set dict, key ->
|
||||||
Dict.remove dict key |> @Set
|
Dict.remove dict key |> @Set
|
||||||
|
|
||||||
|
|
@ -208,7 +215,7 @@ remove = \@Set dict, key ->
|
||||||
## expect hasApple == Bool.true
|
## expect hasApple == Bool.true
|
||||||
## expect hasBanana == Bool.false
|
## expect hasBanana == Bool.false
|
||||||
## ```
|
## ```
|
||||||
contains : Set k, k -> Bool where k implements Hash & Eq
|
contains : Set k, k -> Bool
|
||||||
contains = \@Set dict, key ->
|
contains = \@Set dict, key ->
|
||||||
Dict.contains dict key
|
Dict.contains dict key
|
||||||
|
|
||||||
|
|
@ -221,7 +228,7 @@ contains = \@Set dict, key ->
|
||||||
##
|
##
|
||||||
## expect Set.toList numbers == values
|
## expect Set.toList numbers == values
|
||||||
## ```
|
## ```
|
||||||
toList : Set k -> List k where k implements Hash & Eq
|
toList : Set k -> List k
|
||||||
toList = \@Set dict ->
|
toList = \@Set dict ->
|
||||||
Dict.keys dict
|
Dict.keys dict
|
||||||
|
|
||||||
|
|
@ -235,7 +242,7 @@ toList = \@Set dict ->
|
||||||
##
|
##
|
||||||
## expect Set.fromList [Pear, Apple, Banana] == values
|
## expect Set.fromList [Pear, Apple, Banana] == values
|
||||||
## ```
|
## ```
|
||||||
fromList : List k -> Set k where k implements Hash & Eq
|
fromList : List k -> Set k
|
||||||
fromList = \list ->
|
fromList = \list ->
|
||||||
list
|
list
|
||||||
|> List.map \k -> (k, {})
|
|> List.map \k -> (k, {})
|
||||||
|
|
@ -252,7 +259,7 @@ fromList = \list ->
|
||||||
##
|
##
|
||||||
## expect Set.union set1 set2 == Set.fromList [Left, Right]
|
## 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 ->
|
union = \@Set dict1, @Set dict2 ->
|
||||||
Dict.insertAll dict1 dict2 |> @Set
|
Dict.insertAll dict1 dict2 |> @Set
|
||||||
|
|
||||||
|
|
@ -265,7 +272,7 @@ union = \@Set dict1, @Set dict2 ->
|
||||||
##
|
##
|
||||||
## expect Set.intersection set1 set2 == Set.single Left
|
## 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 ->
|
intersection = \@Set dict1, @Set dict2 ->
|
||||||
Dict.keepShared dict1 dict2 |> @Set
|
Dict.keepShared dict1 dict2 |> @Set
|
||||||
|
|
||||||
|
|
@ -279,7 +286,7 @@ intersection = \@Set dict1, @Set dict2 ->
|
||||||
##
|
##
|
||||||
## expect Set.difference first second == Set.fromList [Up, Down]
|
## 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 ->
|
difference = \@Set dict1, @Set dict2 ->
|
||||||
Dict.removeAll dict1 dict2 |> @Set
|
Dict.removeAll dict1 dict2 |> @Set
|
||||||
|
|
||||||
|
|
@ -302,14 +309,14 @@ difference = \@Set dict1, @Set dict2 ->
|
||||||
##
|
##
|
||||||
## expect result == 2
|
## 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 ->
|
walk = \@Set dict, state, step ->
|
||||||
Dict.walk dict state (\s, k, _ -> step s k)
|
Dict.walk dict state (\s, k, _ -> step s k)
|
||||||
|
|
||||||
## Convert each value in the set to something new, by calling a conversion
|
## 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
|
## function on each of them which receives the old value. Then return a
|
||||||
## new set containing the converted values.
|
## 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 ->
|
map = \set, transform ->
|
||||||
init = withCapacity (capacity set)
|
init = withCapacity (capacity set)
|
||||||
|
|
||||||
|
|
@ -321,7 +328,7 @@ map = \set, transform ->
|
||||||
## (using [Set.union]) into one set.
|
## (using [Set.union]) into one set.
|
||||||
##
|
##
|
||||||
## You may know a similar function named `concatMap` in other languages.
|
## 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 ->
|
joinMap = \set, transform ->
|
||||||
init = withCapacity (capacity set) # Might be a pessimization
|
init = withCapacity (capacity set) # Might be a pessimization
|
||||||
|
|
||||||
|
|
@ -343,7 +350,7 @@ joinMap = \set, transform ->
|
||||||
##
|
##
|
||||||
## expect result == FoundTheAnswer
|
## 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 ->
|
walkUntil = \@Set dict, state, step ->
|
||||||
Dict.walkUntil dict state (\s, k, _ -> step s k)
|
Dict.walkUntil dict state (\s, k, _ -> step s k)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1486,6 +1486,7 @@ define_builtins! {
|
||||||
26 DICT_JOINMAP: "joinMap"
|
26 DICT_JOINMAP: "joinMap"
|
||||||
27 DICT_KEEP_IF: "keepIf"
|
27 DICT_KEEP_IF: "keepIf"
|
||||||
28 DICT_DROP_IF: "dropIf"
|
28 DICT_DROP_IF: "dropIf"
|
||||||
|
29 DICT_RESERVE: "reserve"
|
||||||
}
|
}
|
||||||
9 SET: "Set" => {
|
9 SET: "Set" => {
|
||||||
0 SET_SET: "Set" exposed_type=true // the Set.Set type alias
|
0 SET_SET: "Set" exposed_type=true // the Set.Set type alias
|
||||||
|
|
@ -1510,6 +1511,8 @@ define_builtins! {
|
||||||
19 SET_JOIN_MAP: "joinMap"
|
19 SET_JOIN_MAP: "joinMap"
|
||||||
20 SET_KEEP_IF: "keepIf"
|
20 SET_KEEP_IF: "keepIf"
|
||||||
21 SET_DROP_IF: "dropIf"
|
21 SET_DROP_IF: "dropIf"
|
||||||
|
22 SET_WITH_CAPACITY: "withCapacity"
|
||||||
|
23 SET_RESERVE: "reserve"
|
||||||
}
|
}
|
||||||
10 BOX: "Box" => {
|
10 BOX: "Box" => {
|
||||||
0 BOX_BOX_TYPE: "Box" exposed_apply_type=true // the Box.Box opaque type
|
0 BOX_BOX_TYPE: "Box" exposed_apply_type=true // the Box.Box opaque type
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue