Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HashMapT salt, which allows setting of salt with Nat. #321

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jappeace
Copy link

@jappeace jappeace commented Sep 20, 2021

This allows clients to create custom salted hashmaps.
For backwards compatibility we use

 -- backwards compatibility
type HashMap = HashMapT DefaultSalt

Then modify the functions to be free of salt if they can, for example insert:

insert :: forall k v salt . (Eq k, Hashable k) => k -> v -> HashMapT salt k v -> HashMapT salt k v
insert k v m = insert' (hash salt k) k v m
  where
    salt = natVal (Proxy :: Proxy salt)

This allows the default HashMap with backwards compatibility,
but also any other HashMapT
I think this solves the issue with having different salts in
an intersect:

intersection :: (Eq k, Hashable k) => HashMapT salt k v -> HashMapT salt k w -> HashMapT salt k v

Because salt is the same type variable for all arguments,
it's enforced to be the same.
Then you can also provide a function to resalt if the user ever ends
up with different salts and still wants to do an intersect.
(which results in a reconstruction of the hashmap).

See thread: #319

I know these libraries will at least fail with the changes because they have instances on HashMap,
which now need to be HashMapT salt:

quickcheck-instances-0.3.25.2.drv
semirings-0.6.drv
relude-0.7.0.0.drv
semigroupoids-5.3.5.drv

I did this by stubbing out unordered containers in a 100k loc codebase
to see what issues would come up in CI.

@jappeace
Copy link
Author

jappeace commented Sep 22, 2021

benchmarks (without fixing boxing issues)

I ran cabal new-bench then copy pasted the output next to eachother with vim column paste.

on salty branch                                             |    on master

benchmarked Map/lookup/String                                benchmarked Map/lookup/String
time                 959.8 μs   (952.9 μs .. 968.2 μs)       time                 962.3 μs   (958.3 μs .. 966.9 μs)
                     0.999 R²   (0.999 R² .. 1.000 R²)                            1.000 R²   (0.999 R² .. 1.000 R²)
mean                 964.9 μs   (961.9 μs .. 972.1 μs)       mean                 973.2 μs   (970.0 μs .. 982.0 μs)
std dev              14.38 μs   (5.912 μs .. 27.22 μs)       std dev              16.49 μs   (9.105 μs .. 29.97 μs)

benchmarked Map/lookup/ByteString                            benchmarked Map/lookup/ByteString
time                 653.9 μs   (646.9 μs .. 662.4 μs)       time                 657.7 μs   (653.4 μs .. 662.2 μs)
                     0.999 R²   (0.998 R² .. 1.000 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 662.3 μs   (659.9 μs .. 666.1 μs)       mean                 672.4 μs   (669.7 μs .. 677.6 μs)
std dev              10.24 μs   (7.230 μs .. 16.61 μs)       std dev              11.91 μs   (6.532 μs .. 20.96 μs)

benchmarked Map/lookup-miss/String                           benchmarked Map/lookup-miss/String
time                 1.073 ms   (1.068 ms .. 1.077 ms)       time                 1.068 ms   (1.047 ms .. 1.085 ms)
                     1.000 R²   (0.999 R² .. 1.000 R²)                            0.999 R²   (0.998 R² .. 1.000 R²)
mean                 1.076 ms   (1.071 ms .. 1.080 ms)       mean                 1.078 ms   (1.073 ms .. 1.081 ms)
std dev              14.31 μs   (9.284 μs .. 21.73 μs)       std dev              13.69 μs   (9.518 μs .. 19.60 μs)

benchmarked Map/lookup-miss/ByteString                       benchmarked Map/lookup-miss/ByteString
time                 745.2 μs   (736.5 μs .. 753.3 μs)       time                 759.4 μs   (751.0 μs .. 770.9 μs)
                     0.999 R²   (0.997 R² .. 1.000 R²)                            0.997 R²   (0.993 R² .. 1.000 R²)
mean                 742.3 μs   (739.0 μs .. 751.7 μs)       mean                 749.6 μs   (744.9 μs .. 763.6 μs)
std dev              15.66 μs   (7.854 μs .. 32.39 μs)       std dev              23.49 μs   (10.25 μs .. 47.35 μs)
                                                             variance introduced by outliers: 14% (moderately inflated)
benchmarked Map/insert/String
time                 1.751 ms   (1.736 ms .. 1.767 ms)       benchmarked Map/insert/String
                     0.999 R²   (0.999 R² .. 1.000 R²)       time                 1.736 ms   (1.723 ms .. 1.754 ms)
mean                 1.674 ms   (1.661 ms .. 1.689 ms)                            0.999 R²   (0.998 R² .. 0.999 R²)
std dev              44.61 μs   (40.61 μs .. 51.48 μs)       mean                 1.686 ms   (1.668 ms .. 1.702 ms)
variance introduced by outliers: 11% (moderately inflated)   std dev              55.48 μs   (46.50 μs .. 71.20 μs)
                                                             variance introduced by outliers: 16% (moderately inflated)
benchmarked Map/insert/ByteStringString
time                 1.593 ms   (1.576 ms .. 1.613 ms)       benchmarked Map/insert/ByteStringString
                     0.999 R²   (0.998 R² .. 1.000 R²)       time                 1.610 ms   (1.594 ms .. 1.626 ms)
mean                 1.555 ms   (1.542 ms .. 1.570 ms)                            0.998 R²   (0.996 R² .. 0.999 R²)
std dev              46.05 μs   (38.99 μs .. 61.16 μs)       mean                 1.570 ms   (1.555 ms .. 1.594 ms)
variance introduced by outliers: 13% (moderately inflated)   std dev              61.64 μs   (40.04 μs .. 116.8 μs)
                                                             variance introduced by outliers: 20% (moderately inflated)
benchmarked Map/insert-dup/String
time                 1.271 ms   (1.267 ms .. 1.275 ms)       benchmarked Map/insert-dup/String
                     1.000 R²   (1.000 R² .. 1.000 R²)       time                 1.248 ms   (1.243 ms .. 1.257 ms)
mean                 1.275 ms   (1.273 ms .. 1.281 ms)                            1.000 R²   (0.999 R² .. 1.000 R²)
std dev              11.26 μs   (6.741 μs .. 20.92 μs)       mean                 1.256 ms   (1.253 ms .. 1.260 ms)
                                                             std dev              10.87 μs   (6.880 μs .. 16.00 μs)
benchmarked Map/insert-dup/ByteStringString
time                 948.7 μs   (940.2 μs .. 959.8 μs)       benchmarked Map/insert-dup/ByteStringString
                     1.000 R²   (0.999 R² .. 1.000 R²)       time                 944.9 μs   (930.0 μs .. 967.3 μs)
mean                 957.7 μs   (955.1 μs .. 959.6 μs)                            0.999 R²   (0.998 R² .. 1.000 R²)
std dev              7.493 μs   (5.471 μs .. 10.15 μs)       mean                 962.6 μs   (958.7 μs .. 967.0 μs)
                                                             std dev              13.70 μs   (10.13 μs .. 17.93 μs)
benchmarked Map/delete/String
time                 1.452 ms   (1.449 ms .. 1.455 ms)       benchmarked Map/delete/String
                     1.000 R²   (1.000 R² .. 1.000 R²)       time                 1.436 ms   (1.423 ms .. 1.448 ms)
mean                 1.461 ms   (1.458 ms .. 1.464 ms)                            0.999 R²   (0.999 R² .. 1.000 R²)
std dev              9.091 μs   (7.061 μs .. 13.92 μs)       mean                 1.480 ms   (1.464 ms .. 1.501 ms)
                                                             std dev              60.10 μs   (43.65 μs .. 75.83 μs)
benchmarked Map/delete/ByteString                            variance introduced by outliers: 22% (moderately inflated)
time                 1.185 ms   (1.183 ms .. 1.187 ms)
                     1.000 R²   (1.000 R² .. 1.000 R²)       benchmarked Map/delete/ByteString
mean                 1.187 ms   (1.186 ms .. 1.188 ms)       time                 1.194 ms   (1.185 ms .. 1.206 ms)
std dev              4.636 μs   (3.798 μs .. 5.916 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
                                                             mean                 1.191 ms   (1.188 ms .. 1.194 ms)
benchmarked Map/delete-miss/String                           std dev              9.804 μs   (5.831 μs .. 16.02 μs)
time                 1.314 ms   (1.305 ms .. 1.319 ms)
                     1.000 R²   (0.999 R² .. 1.000 R²)       benchmarked Map/delete-miss/String
mean                 1.321 ms   (1.317 ms .. 1.324 ms)       time                 1.330 ms   (1.319 ms .. 1.339 ms)
std dev              11.61 μs   (8.196 μs .. 16.89 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
                                                             mean                 1.320 ms   (1.317 ms .. 1.323 ms)
benchmarked Map/delete-miss/ByteString                       std dev              10.89 μs   (7.777 μs .. 16.69 μs)
time                 997.1 μs   (994.9 μs .. 999.3 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       benchmarked Map/delete-miss/ByteString
mean                 993.5 μs   (991.8 μs .. 994.8 μs)       time                 1.001 ms   (991.0 μs .. 1.012 ms)
std dev              4.959 μs   (3.669 μs .. 7.003 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
                                                             mean                 988.8 μs   (985.8 μs .. 992.4 μs)
benchmarked Map/size/String                                  std dev              10.87 μs   (8.543 μs .. 14.31 μs)
time                 4.366 ns   (4.317 ns .. 4.478 ns)
                     0.997 R²   (0.991 R² .. 1.000 R²)       benchmarked Map/size/String
mean                 4.374 ns   (4.356 ns .. 4.432 ns)       time                 4.340 ns   (4.331 ns .. 4.350 ns)
std dev              71.32 ps   (25.59 ps .. 136.9 ps)                            1.000 R²   (1.000 R² .. 1.000 R²)
                                                             mean                 4.340 ns   (4.336 ns .. 4.358 ns)
benchmarked Map/size/ByteString                              std dev              17.95 ps   (2.993 ps .. 36.58 ps)
time                 4.339 ns   (4.326 ns .. 4.348 ns)
                     1.000 R²   (1.000 R² .. 1.000 R²)       benchmarked Map/size/ByteString
mean                 4.350 ns   (4.346 ns .. 4.361 ns)       time                 4.330 ns   (4.325 ns .. 4.336 ns)
std dev              16.53 ps   (7.503 ps .. 28.81 ps)                            1.000 R²   (1.000 R² .. 1.000 R²)
                                                             mean                 4.340 ns   (4.338 ns .. 4.343 ns)
benchmarked Map/fromList/String                              std dev              6.692 ps   (3.993 ps .. 10.80 ps)
time                 1.770 ms   (1.742 ms .. 1.802 ms)
                     0.999 R²   (0.998 R² .. 1.000 R²)       benchmarked Map/fromList/String
mean                 1.702 ms   (1.690 ms .. 1.716 ms)       time                 1.729 ms   (1.720 ms .. 1.740 ms)
std dev              42.62 μs   (36.98 μs .. 55.28 μs)                            0.999 R²   (0.999 R² .. 1.000 R²)
                                                             mean                 1.682 ms   (1.670 ms .. 1.693 ms)
benchmarked Map/fromList/ByteString                          std dev              37.56 μs   (34.20 μs .. 43.15 μs)
time                 1.594 ms   (1.579 ms .. 1.609 ms)
                     0.999 R²   (0.999 R² .. 1.000 R²)       benchmarked Map/fromList/ByteString
mean                 1.528 ms   (1.513 ms .. 1.540 ms)       time                 1.599 ms   (1.582 ms .. 1.615 ms)
std dev              42.63 μs   (38.96 μs .. 47.26 μs)                            0.999 R²   (0.998 R² .. 1.000 R²)
variance introduced by outliers: 11% (moderately inflated)   mean                 1.532 ms   (1.518 ms .. 1.547 ms)
                                                             std dev              47.01 μs   (40.77 μs .. 59.08 μs)
benchmarked Map/isSubmapOf/String                            variance introduced by outliers: 14% (moderately inflated)
time                 140.2 μs   (139.7 μs .. 140.8 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       benchmarked Map/isSubmapOf/String
mean                 140.7 μs   (140.4 μs .. 141.0 μs)       time                 143.9 μs   (141.0 μs .. 146.9 μs)
std dev              845.9 ns   (635.8 ns .. 1.189 μs)                            0.997 R²   (0.993 R² .. 1.000 R²)
                                                             mean                 141.1 μs   (140.5 μs .. 142.7 μs)
benchmarked Map/isSubmapOf/ByteString                        std dev              2.749 μs   (1.050 μs .. 5.393 μs)
time                 75.08 μs   (74.88 μs .. 75.26 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       benchmarked Map/isSubmapOf/ByteString
mean                 75.23 μs   (75.13 μs .. 75.53 μs)       time                 76.22 μs   (75.40 μs .. 77.11 μs)
std dev              499.8 ns   (204.5 ns .. 1.008 μs)                            0.999 R²   (0.999 R² .. 1.000 R²)
                                                             mean                 76.71 μs   (76.40 μs .. 77.31 μs)
benchmarked hashmap/Map/lookup/String                        std dev              1.333 μs   (792.7 ns .. 2.175 μs)
time                 630.3 μs   (628.8 μs .. 631.9 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       benchmarked hashmap/Map/lookup/String
mean                 633.2 μs   (632.3 μs .. 635.1 μs)       time                 624.6 μs   (621.0 μs .. 628.7 μs)
std dev              4.353 μs   (2.374 μs .. 7.758 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
                                                             mean                 636.1 μs   (633.7 μs .. 639.8 μs)
benchmarked hashmap/Map/lookup/ByteString                    std dev              9.173 μs   (7.026 μs .. 14.31 μs)
time                 413.8 μs   (412.5 μs .. 415.8 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       benchmarked hashmap/Map/lookup/ByteString
mean                 414.8 μs   (414.3 μs .. 416.3 μs)       time                 411.9 μs   (409.7 μs .. 413.6 μs)
std dev              2.901 μs   (1.670 μs .. 5.178 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
                                                             mean                 415.6 μs   (414.2 μs .. 419.7 μs)
benchmarked hashmap/Map/lookup-miss/String                   std dev              7.490 μs   (2.376 μs .. 15.24 μs)
time                 406.6 μs   (404.3 μs .. 409.0 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       benchmarked hashmap/Map/lookup-miss/String
mean                 406.1 μs   (405.6 μs .. 406.7 μs)       time                 407.5 μs   (404.3 μs .. 412.2 μs)
std dev              1.837 μs   (1.584 μs .. 2.185 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
                                                             mean                 406.7 μs   (405.8 μs .. 408.4 μs)
benchmarked hashmap/Map/lookup-miss/ByteString               std dev              3.667 μs   (2.100 μs .. 5.994 μs)
time                 338.6 μs   (337.4 μs .. 339.8 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)       benchmarked hashmap/Map/lookup-miss/ByteString
mean                 344.2 μs   (343.0 μs .. 346.1 μs)       time                 338.7 μs   (337.4 μs .. 340.3 μs)
std dev              4.851 μs   (2.890 μs .. 7.247 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
                                                             mean                 342.6 μs   (341.8 μs .. 343.7 μs)
benchmarked hashmap/Map/insert/String                        std dev              3.224 μs   (2.476 μs .. 4.249 μs)
time                 1.538 ms   (1.520 ms .. 1.556 ms)
                     0.999 R²   (0.999 R² .. 0.999 R²)       benchmarked hashmap/Map/insert/String
mean                 1.411 ms   (1.388 ms .. 1.432 ms)       time                 1.517 ms   (1.507 ms .. 1.529 ms)
std dev              66.82 μs   (59.42 μs .. 74.47 μs)                            0.999 R²   (0.999 R² .. 1.000 R²)
variance introduced by outliers: 24% (moderately inflated)   mean                 1.411 ms   (1.391 ms .. 1.430 ms)
                                                             std dev              63.22 μs   (56.47 μs .. 69.86 μs)
benchmarked hashmap/Map/insert/ByteStringString              variance introduced by outliers: 22% (moderately inflated)
time                 1.217 ms   (1.208 ms .. 1.225 ms)
                     0.999 R²   (0.999 R² .. 1.000 R²)       benchmarked hashmap/Map/insert/ByteStringString
mean                 1.206 ms   (1.198 ms .. 1.215 ms)       time                 1.205 ms   (1.196 ms .. 1.217 ms)
std dev              26.47 μs   (21.45 μs .. 35.02 μs)                            0.999 R²   (0.999 R² .. 1.000 R²)
                                                             mean                 1.196 ms   (1.188 ms .. 1.204 ms)
benchmarked hashmap/Map/insert-dup/String                    std dev              28.05 μs   (22.12 μs .. 36.69 μs)
time                 2.152 ms   (2.115 ms .. 2.186 ms)
                     0.998 R²   (0.997 R² .. 0.999 R²)       benchmarked hashmap/Map/insert-dup/String
mean                 2.093 ms   (2.076 ms .. 2.109 ms)       time                 2.107 ms   (2.088 ms .. 2.127 ms)
std dev              56.70 μs   (45.27 μs .. 72.31 μs)                            0.999 R²   (0.999 R² .. 0.999 R²)
variance introduced by outliers: 11% (moderately inflated)   mean                 2.077 ms   (2.061 ms .. 2.090 ms)
                                                             std dev              47.67 μs   (38.65 μs .. 59.33 μs)
benchmarked hashmap/Map/insert-dup/ByteStringString
time                 2.005 ms   (1.983 ms .. 2.025 ms)       benchmarked hashmap/Map/insert-dup/ByteStringString
                     0.999 R²   (0.998 R² .. 0.999 R²)       time                 2.053 ms   (2.026 ms .. 2.085 ms)
mean                 1.931 ms   (1.913 ms .. 1.953 ms)                            0.998 R²   (0.997 R² .. 0.999 R²)
std dev              66.76 μs   (52.28 μs .. 95.54 μs)       mean                 1.946 ms   (1.922 ms .. 1.970 ms)
variance introduced by outliers: 18% (moderately inflated)   std dev              80.06 μs   (66.96 μs .. 96.40 μs)
                                                             variance introduced by outliers: 21% (moderately inflated)
benchmarked hashmap/Map/delete/String
time                 887.2 μs   (884.1 μs .. 891.4 μs)       benchmarked hashmap/Map/delete/String
                     1.000 R²   (1.000 R² .. 1.000 R²)       time                 858.5 μs   (852.3 μs .. 862.9 μs)
mean                 888.8 μs   (887.9 μs .. 890.2 μs)                            0.999 R²   (0.999 R² .. 1.000 R²)
std dev              3.627 μs   (2.775 μs .. 5.551 μs)       mean                 875.5 μs   (870.5 μs .. 884.3 μs)
                                                             std dev              21.42 μs   (13.39 μs .. 31.89 μs)
benchmarked hashmap/Map/delete/ByteString
time                 656.8 μs   (655.6 μs .. 657.8 μs)       benchmarked hashmap/Map/delete/ByteString
                     1.000 R²   (1.000 R² .. 1.000 R²)       time                 650.7 μs   (650.1 μs .. 651.5 μs)
mean                 657.0 μs   (656.7 μs .. 657.6 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
std dev              1.483 μs   (1.235 μs .. 2.086 μs)       mean                 653.2 μs   (652.8 μs .. 653.8 μs)
                                                             std dev              1.679 μs   (1.325 μs .. 2.282 μs)
benchmarked hashmap/Map/delete-miss/String
time                 845.3 μs   (836.2 μs .. 853.3 μs)       benchmarked hashmap/Map/delete-miss/String
                     0.999 R²   (0.997 R² .. 1.000 R²)       time                 853.2 μs   (846.6 μs .. 861.8 μs)
mean                 817.6 μs   (810.1 μs .. 829.0 μs)                            0.998 R²   (0.995 R² .. 1.000 R²)
std dev              29.98 μs   (20.86 μs .. 52.85 μs)       mean                 816.1 μs   (807.2 μs .. 826.9 μs)
variance introduced by outliers: 18% (moderately inflated)   std dev              32.12 μs   (26.32 μs .. 47.29 μs)
                                                             variance introduced by outliers: 19% (moderately inflated)
benchmarked hashmap/Map/delete-miss/ByteString
time                 798.5 μs   (791.1 μs .. 806.8 μs)       benchmarked hashmap/Map/delete-miss/ByteString
                     0.999 R²   (0.999 R² .. 1.000 R²)       time                 803.3 μs   (796.5 μs .. 810.6 μs)
mean                 754.4 μs   (743.7 μs .. 763.9 μs)                            0.999 R²   (0.999 R² .. 1.000 R²)
std dev              31.57 μs   (25.93 μs .. 35.33 μs)       mean                 765.5 μs   (755.8 μs .. 774.5 μs)
variance introduced by outliers: 22% (moderately inflated)   std dev              30.62 μs   (25.56 μs .. 34.26 μs)
                                                             variance introduced by outliers: 19% (moderately inflated)
benchmarked hashmap/Map/size/String
time                 22.68 μs   (22.35 μs .. 23.37 μs)       benchmarked hashmap/Map/size/String
                     0.993 R²   (0.984 R² .. 1.000 R²)       time                 24.57 μs   (24.44 μs .. 24.74 μs)
mean                 22.49 μs   (22.31 μs .. 22.87 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
std dev              748.7 ns   (102.6 ns .. 1.328 μs)       mean                 24.58 μs   (24.54 μs .. 24.67 μs)
variance introduced by outliers: 13% (moderately inflated)   std dev              175.1 ns   (97.79 ns .. 275.5 ns)

benchmarked hashmap/Map/size/ByteString                      benchmarked hashmap/Map/size/ByteString
time                 22.66 μs   (22.52 μs .. 22.86 μs)       time                 24.81 μs   (24.73 μs .. 24.91 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 22.75 μs   (22.63 μs .. 22.81 μs)       mean                 24.92 μs   (24.85 μs .. 24.99 μs)
std dev              242.3 ns   (134.5 ns .. 388.2 ns)       std dev              237.2 ns   (101.2 ns .. 416.3 ns)

benchmarked hashmap/Map/fromList/String                      benchmarked hashmap/Map/fromList/String
time                 1.544 ms   (1.529 ms .. 1.555 ms)       time                 1.549 ms   (1.536 ms .. 1.563 ms)
                     0.999 R²   (0.999 R² .. 1.000 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 1.486 ms   (1.474 ms .. 1.497 ms)       mean                 1.500 ms   (1.489 ms .. 1.511 ms)
std dev              35.93 μs   (30.47 μs .. 43.72 μs)       std dev              36.07 μs   (30.88 μs .. 45.05 μs)

benchmarked hashmap/Map/fromList/ByteString                  benchmarked hashmap/Map/fromList/ByteString
time                 1.240 ms   (1.225 ms .. 1.256 ms)       time                 1.253 ms   (1.242 ms .. 1.265 ms)
                     0.999 R²   (0.998 R² .. 0.999 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 1.203 ms   (1.191 ms .. 1.212 ms)       mean                 1.210 ms   (1.200 ms .. 1.219 ms)
std dev              34.56 μs   (28.55 μs .. 42.52 μs)       std dev              33.59 μs   (28.20 μs .. 39.81 μs)
variance introduced by outliers: 13% (moderately inflated)   variance introduced by outliers: 11% (moderately inflated)

benchmarked hashmap/Map/isSubmapOf/String                    benchmarked hashmap/Map/isSubmapOf/String
time                 120.5 μs   (120.2 μs .. 120.7 μs)       time                 122.2 μs   (121.9 μs .. 122.6 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 120.9 μs   (120.7 μs .. 121.4 μs)       mean                 122.3 μs   (122.2 μs .. 122.4 μs)
std dev              780.9 ns   (231.6 ns .. 1.587 μs)       std dev              335.5 ns   (280.7 ns .. 404.0 ns)

benchmarked hashmap/Map/isSubmapOf/ByteString                benchmarked hashmap/Map/isSubmapOf/ByteString
time                 71.89 μs   (71.72 μs .. 72.09 μs)       time                 76.03 μs   (75.79 μs .. 76.24 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 72.71 μs   (72.59 μs .. 72.83 μs)       mean                 76.90 μs   (76.73 μs .. 77.10 μs)
std dev              407.9 ns   (349.5 ns .. 475.4 ns)       std dev              597.1 ns   (505.2 ns .. 732.0 ns)

benchmarked hashmap/Map/hash/String                          benchmarked hashmap/Map/hash/String
time                 51.10 μs   (50.90 μs .. 51.38 μs)       time                 52.57 μs   (52.37 μs .. 52.80 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 51.01 μs   (50.93 μs .. 51.12 μs)       mean                 52.64 μs   (52.57 μs .. 52.74 μs)
std dev              270.3 ns   (193.0 ns .. 467.3 ns)       std dev              263.2 ns   (221.0 ns .. 326.4 ns)

benchmarked hashmap/Map/hash/ByteString                      benchmarked hashmap/Map/hash/ByteString
time                 50.98 μs   (50.57 μs .. 51.45 μs)       time                 52.73 μs   (52.44 μs .. 53.03 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 50.61 μs   (50.51 μs .. 50.75 μs)       mean                 52.44 μs   (52.37 μs .. 52.53 μs)
std dev              367.3 ns   (260.6 ns .. 489.4 ns)       std dev              270.4 ns   (204.1 ns .. 346.5 ns)

benchmarked IntMap/lookup                                    benchmarked IntMap/lookup
time                 310.3 μs   (307.0 μs .. 312.2 μs)       time                 308.8 μs   (308.2 μs .. 309.4 μs)
                     0.999 R²   (0.996 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 314.1 μs   (312.5 μs .. 318.8 μs)       mean                 312.4 μs   (311.8 μs .. 313.1 μs)
std dev              9.272 μs   (1.440 μs .. 17.76 μs)       std dev              2.042 μs   (1.756 μs .. 2.467 μs)
variance introduced by outliers: 12% (moderately inflated)
                                                             benchmarked IntMap/lookup-miss
benchmarked IntMap/lookup-miss                               time                 304.2 μs   (303.4 μs .. 305.4 μs)
time                 304.8 μs   (304.4 μs .. 305.3 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
                     1.000 R²   (1.000 R² .. 1.000 R²)       mean                 305.3 μs   (304.9 μs .. 305.7 μs)
mean                 305.8 μs   (305.6 μs .. 306.1 μs)       std dev              1.272 μs   (992.9 ns .. 1.676 μs)
std dev              832.8 ns   (656.8 ns .. 1.102 μs)
                                                             benchmarked IntMap/insert
benchmarked IntMap/insert                                    time                 860.7 μs   (849.9 μs .. 869.9 μs)
time                 859.9 μs   (850.3 μs .. 870.1 μs)                            0.999 R²   (0.998 R² .. 0.999 R²)
                     0.999 R²   (0.998 R² .. 0.999 R²)       mean                 828.9 μs   (818.5 μs .. 836.8 μs)
mean                 823.2 μs   (809.9 μs .. 831.4 μs)       std dev              28.99 μs   (20.20 μs .. 39.69 μs)
std dev              32.50 μs   (22.53 μs .. 42.84 μs)       variance introduced by outliers: 15% (moderately inflated)
variance introduced by outliers: 18% (moderately inflated)
                                                             benchmarked IntMap/insert-dup
benchmarked IntMap/insert-dup                                time                 1.014 ms   (1.001 ms .. 1.028 ms)
time                 1.010 ms   (998.1 μs .. 1.022 ms)                            0.999 R²   (0.998 R² .. 0.999 R²)
                     0.999 R²   (0.999 R² .. 0.999 R²)       mean                 972.1 μs   (956.5 μs .. 982.4 μs)
mean                 968.2 μs   (952.4 μs .. 978.5 μs)       std dev              39.72 μs   (30.39 μs .. 49.44 μs)
std dev              39.92 μs   (28.02 μs .. 48.84 μs)       variance introduced by outliers: 20% (moderately inflated)
variance introduced by outliers: 20% (moderately inflated)
                                                             benchmarked IntMap/delete
benchmarked IntMap/delete                                    time                 516.4 μs   (515.8 μs .. 517.1 μs)
time                 514.6 μs   (513.5 μs .. 516.3 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
                     1.000 R²   (1.000 R² .. 1.000 R²)       mean                 517.9 μs   (517.5 μs .. 518.2 μs)
mean                 515.6 μs   (515.2 μs .. 516.2 μs)       std dev              1.174 μs   (988.9 ns .. 1.385 μs)
std dev              1.485 μs   (929.5 ns .. 2.254 μs)
                                                             benchmarked IntMap/delete-miss
benchmarked IntMap/delete-miss                               time                 661.2 μs   (657.5 μs .. 667.2 μs)
time                 656.4 μs   (652.8 μs .. 661.8 μs)                            0.999 R²   (0.998 R² .. 1.000 R²)
                     0.999 R²   (0.999 R² .. 1.000 R²)       mean                 642.8 μs   (639.9 μs .. 646.8 μs)
mean                 639.1 μs   (636.3 μs .. 642.9 μs)       std dev              11.85 μs   (9.625 μs .. 16.86 μs)
std dev              10.55 μs   (8.920 μs .. 12.28 μs)
                                                             benchmarked IntMap/size
benchmarked IntMap/size                                      time                 12.80 μs   (12.73 μs .. 12.87 μs)
time                 13.20 μs   (12.99 μs .. 13.42 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
                     0.999 R²   (0.998 R² .. 1.000 R²)       mean                 12.83 μs   (12.78 μs .. 12.86 μs)
mean                 13.25 μs   (13.20 μs .. 13.35 μs)       std dev              106.5 ns   (64.46 ns .. 157.0 ns)
std dev              225.2 ns   (164.4 ns .. 309.6 ns)
                                                             benchmarked IntMap/fromList
benchmarked IntMap/fromList                                  time                 863.6 μs   (853.2 μs .. 872.8 μs)
time                 876.5 μs   (856.7 μs .. 905.1 μs)                            0.999 R²   (0.998 R² .. 0.999 R²)
                     0.996 R²   (0.989 R² .. 0.999 R²)       mean                 823.0 μs   (810.1 μs .. 831.5 μs)
mean                 824.9 μs   (811.2 μs .. 836.2 μs)       std dev              32.93 μs   (23.21 μs .. 43.20 μs)
std dev              39.31 μs   (27.80 μs .. 54.32 μs)       variance introduced by outliers: 20% (moderately inflated)
variance introduced by outliers: 25% (moderately inflated)
                                                             benchmarked IntMap/isSubmapOf
benchmarked IntMap/isSubmapOf                                time                 103.5 ns   (101.4 ns .. 105.6 ns)
time                 104.2 ns   (103.0 ns .. 106.1 ns)                            0.998 R²   (0.996 R² .. 0.999 R²)
                     0.999 R²   (0.998 R² .. 0.999 R²)       mean                 102.6 ns   (101.9 ns .. 103.3 ns)
mean                 104.7 ns   (104.1 ns .. 105.3 ns)       std dev              1.933 ns   (1.519 ns .. 2.512 ns)
std dev              1.677 ns   (1.386 ns .. 2.105 ns)
                                                             
benchmarked HashMap/lookup/String                            benchmarked HashMap/lookup/String
time                 517.3 μs   (514.8 μs .. 520.2 μs)       time                 526.4 μs   (524.2 μs .. 528.5 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 533.8 μs   (531.5 μs .. 536.1 μs)       mean                 535.1 μs   (533.3 μs .. 538.4 μs)
std dev              8.052 μs   (6.148 μs .. 11.86 μs)       std dev              7.714 μs   (5.008 μs .. 13.27 μs)
                                                                                                                       
benchmarked HashMap/lookup/ByteString                        benchmarked HashMap/lookup/ByteString
time                 207.4 μs   (199.4 μs .. 212.8 μs)       time                 206.3 μs   (205.0 μs .. 208.1 μs)
                     0.993 R²   (0.989 R² .. 0.997 R²)                            0.999 R²   (0.998 R² .. 1.000 R²)
mean                 206.6 μs   (204.7 μs .. 208.6 μs)       mean                 206.4 μs   (205.3 μs .. 207.2 μs)
std dev              6.274 μs   (4.888 μs .. 7.606 μs)       std dev              3.059 μs   (2.213 μs .. 4.202 μs)
variance introduced by outliers: 12% (moderately inflated)                                                             
                                                             benchmarked HashMap/lookup/Int
benchmarked HashMap/lookup/Int                               time                 105.7 μs   (105.1 μs .. 106.3 μs)
time                 116.2 μs   (114.5 μs .. 117.5 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
                     0.999 R²   (0.998 R² .. 0.999 R²)       mean                 105.7 μs   (105.5 μs .. 106.1 μs)
mean                 113.8 μs   (112.6 μs .. 114.5 μs)       std dev              996.2 ns   (827.9 ns .. 1.181 μs)
std dev              2.989 μs   (2.143 μs .. 4.151 μs)                                                                 
                                                             
benchmarked HashMap/lookup-miss/String                       benchmarked HashMap/lookup-miss/String
time                 207.6 μs   (207.0 μs .. 208.1 μs)       time                 204.8 μs   (203.1 μs .. 206.6 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 206.6 μs   (206.3 μs .. 206.9 μs)       mean                 206.3 μs   (205.6 μs .. 208.0 μs)
std dev              923.4 ns   (763.8 ns .. 1.187 μs)       std dev              3.321 μs   (1.323 μs .. 6.570 μs)
                                                                                                                       
benchmarked HashMap/lookup-miss/ByteString                   benchmarked HashMap/lookup-miss/ByteString
time                 118.5 μs   (117.7 μs .. 119.2 μs)       time                 117.2 μs   (116.2 μs .. 118.3 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 118.2 μs   (118.1 μs .. 118.4 μs)       mean                 116.7 μs   (116.5 μs .. 117.4 μs)
std dev              573.5 ns   (456.7 ns .. 785.7 ns)       std dev              1.199 μs   (580.1 ns .. 2.368 μs)
                                                                                                                       
benchmarked HashMap/lookup-miss/Int                          benchmarked HashMap/lookup-miss/Int
time                 106.1 μs   (105.9 μs .. 106.4 μs)       time                 100.4 μs   (99.76 μs .. 101.1 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            0.999 R²   (0.998 R² .. 1.000 R²)
mean                 106.5 μs   (106.4 μs .. 106.7 μs)       mean                 99.63 μs   (99.27 μs .. 100.2 μs)
std dev              461.7 ns   (391.7 ns .. 556.4 ns)       std dev              1.535 μs   (808.6 ns .. 2.500 μs)
                                                                                                                       
benchmarked HashMap/insert/String                            benchmarked HashMap/insert/String
time                 1.054 ms   (1.025 ms .. 1.083 ms)       time                 1.014 ms   (986.4 μs .. 1.048 ms)
                     0.994 R²   (0.989 R² .. 0.997 R²)                            0.992 R²   (0.986 R² .. 0.996 R²)
mean                 975.9 μs   (954.5 μs .. 995.1 μs)       mean                 975.5 μs   (955.3 μs .. 992.6 μs)
std dev              63.83 μs   (54.16 μs .. 76.96 μs)       std dev              58.25 μs   (46.84 μs .. 72.46 μs)
variance introduced by outliers: 39% (moderately inflated)   variance introduced by outliers: 35% (moderately inflated)
                                                                                                                       
benchmarked HashMap/insert/ByteString                        benchmarked HashMap/insert/ByteString
time                 1.033 ms   (1.024 ms .. 1.043 ms)       time                 1.025 ms   (1.014 ms .. 1.034 ms)
                     0.999 R²   (0.999 R² .. 1.000 R²)                            0.999 R²   (0.999 R² .. 0.999 R²)
mean                 1.004 ms   (994.1 μs .. 1.011 ms)       mean                 1.001 ms   (991.6 μs .. 1.009 ms)
std dev              30.10 μs   (24.18 μs .. 37.73 μs)       std dev              28.20 μs   (22.72 μs .. 36.59 μs)
variance introduced by outliers: 13% (moderately inflated)   variance introduced by outliers: 11% (moderately inflated)
                                                                                                                       
benchmarked HashMap/insert/Int                               benchmarked HashMap/insert/Int
time                 708.4 μs   (702.2 μs .. 714.5 μs)       time                 681.4 μs   (675.9 μs .. 686.5 μs)
                     0.999 R²   (0.999 R² .. 1.000 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 685.8 μs   (679.0 μs .. 691.3 μs)       mean                 673.2 μs   (668.4 μs .. 677.7 μs)
std dev              19.41 μs   (15.64 μs .. 26.80 μs)       std dev              15.79 μs   (12.34 μs .. 21.29 μs)
variance introduced by outliers: 11% (moderately inflated)                                                             
                                                             
benchmarked HashMap/insert-dup/String                        benchmarked HashMap/insert-dup/String
time                 497.4 μs   (494.7 μs .. 499.5 μs)       time                 551.3 μs   (549.5 μs .. 552.8 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 502.9 μs   (501.5 μs .. 507.1 μs)       mean                 554.3 μs   (553.3 μs .. 557.1 μs)
std dev              6.972 μs   (3.052 μs .. 13.51 μs)       std dev              4.843 μs   (2.387 μs .. 9.580 μs)
                                                                                                                       
benchmarked HashMap/insert-dup/ByteString                    benchmarked HashMap/insert-dup/ByteString
time                 258.2 μs   (255.7 μs .. 261.4 μs)       time                 239.5 μs   (236.7 μs .. 243.2 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)                            0.998 R²   (0.995 R² .. 1.000 R²)
mean                 257.7 μs   (257.2 μs .. 258.4 μs)       mean                 240.2 μs   (239.2 μs .. 243.0 μs)
std dev              1.791 μs   (1.313 μs .. 2.714 μs)       std dev              5.070 μs   (1.480 μs .. 10.11 μs)
                                                                                                                       
benchmarked HashMap/insert-dup/Int                           benchmarked HashMap/insert-dup/Int
time                 280.2 μs   (279.6 μs .. 280.7 μs)       time                 271.0 μs   (266.9 μs .. 274.5 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 280.9 μs   (280.7 μs .. 281.2 μs)       mean                 275.1 μs   (273.8 μs .. 275.9 μs)
std dev              920.9 ns   (767.6 ns .. 1.141 μs)       std dev              3.258 μs   (1.966 μs .. 4.834 μs)
                                                                                                                       
benchmarked HashMap/delete/String                            benchmarked HashMap/delete/String
time                 781.2 μs   (778.5 μs .. 785.1 μs)       time                 792.8 μs   (791.2 μs .. 794.3 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 784.9 μs   (783.8 μs .. 786.4 μs)       mean                 797.2 μs   (795.8 μs .. 799.2 μs)
std dev              4.133 μs   (3.446 μs .. 5.194 μs)       std dev              5.403 μs   (3.582 μs .. 8.453 μs)
                                                                                                                       
benchmarked HashMap/delete/ByteString                        benchmarked HashMap/delete/ByteString
time                 488.0 μs   (487.1 μs .. 489.1 μs)       time                 463.3 μs   (462.9 μs .. 463.8 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 489.0 μs   (488.5 μs .. 489.7 μs)       mean                 465.9 μs   (465.4 μs .. 466.2 μs)
std dev              1.929 μs   (1.532 μs .. 2.481 μs)       std dev              1.288 μs   (1.095 μs .. 1.537 μs)
                                                                                                                       
benchmarked HashMap/delete/Int                               benchmarked HashMap/delete/Int
time                 315.8 μs   (312.1 μs .. 322.0 μs)       time                 323.4 μs   (322.3 μs .. 324.3 μs)
                     0.998 R²   (0.995 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 315.1 μs   (313.9 μs .. 317.6 μs)       mean                 323.8 μs   (323.5 μs .. 324.1 μs)
std dev              5.122 μs   (2.744 μs .. 10.34 μs)       std dev              943.6 ns   (756.2 ns .. 1.177 μs)
                                                                                                                       
benchmarked HashMap/delete-miss/String                       benchmarked HashMap/delete-miss/String
time                 221.6 μs   (220.5 μs .. 222.6 μs)       time                 215.6 μs   (213.9 μs .. 217.7 μs)
                     1.000 R²   (0.998 R² .. 1.000 R²)                            1.000 R²   (0.999 R² .. 1.000 R²)
mean                 224.1 μs   (223.2 μs .. 225.9 μs)       mean                 217.3 μs   (216.7 μs .. 218.2 μs)
std dev              4.100 μs   (830.6 ns .. 6.942 μs)       std dev              2.485 μs   (1.728 μs .. 3.928 μs)
                                                                                                                       
benchmarked HashMap/delete-miss/ByteString                   benchmarked HashMap/delete-miss/ByteString
time                 151.1 μs   (150.7 μs .. 151.7 μs)       time                 148.7 μs   (146.9 μs .. 151.2 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            0.997 R²   (0.990 R² .. 1.000 R²)
mean                 150.9 μs   (150.7 μs .. 151.1 μs)       mean                 148.8 μs   (148.1 μs .. 150.7 μs)
std dev              588.3 ns   (454.4 ns .. 775.0 ns)       std dev              3.344 μs   (1.126 μs .. 6.738 μs)
                                                                                                                       
benchmarked HashMap/delete-miss/Int                          benchmarked HashMap/delete-miss/Int
time                 205.3 μs   (204.9 μs .. 205.7 μs)       time                 204.3 μs   (203.9 μs .. 205.0 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 205.0 μs   (204.8 μs .. 205.4 μs)       mean                 205.8 μs   (205.4 μs .. 206.3 μs)
std dev              882.0 ns   (541.5 ns .. 1.559 μs)       std dev              1.308 μs   (998.8 ns .. 1.937 μs)
                                                                                                                       
benchmarked HashMap/alterInsert/String                       benchmarked HashMap/alterInsert/String
time                 1.025 ms   (1.002 ms .. 1.048 ms)       time                 1.036 ms   (1.012 ms .. 1.058 ms)
                     0.994 R²   (0.990 R² .. 0.997 R²)                            0.997 R²   (0.994 R² .. 0.998 R²)
mean                 949.5 μs   (929.6 μs .. 965.7 μs)       mean                 956.5 μs   (938.6 μs .. 970.9 μs)
std dev              54.52 μs   (44.43 μs .. 72.93 μs)       std dev              50.97 μs   (39.58 μs .. 76.00 μs)
variance introduced by outliers: 33% (moderately inflated)   variance introduced by outliers: 28% (moderately inflated)
                                                                                                                       
benchmarked HashMap/alterInsert/ByteString                   benchmarked HashMap/alterInsert/ByteString
time                 1.050 ms   (1.031 ms .. 1.065 ms)       time                 1.051 ms   (1.043 ms .. 1.059 ms)
                     0.999 R²   (0.998 R² .. 0.999 R²)                            0.999 R²   (0.998 R² .. 1.000 R²)
mean                 1.004 ms   (993.5 μs .. 1.012 ms)       mean                 1.027 ms   (1.016 ms .. 1.035 ms)
std dev              31.96 μs   (26.97 μs .. 38.80 μs)       std dev              30.52 μs   (24.17 μs .. 38.63 μs)
variance introduced by outliers: 14% (moderately inflated)   variance introduced by outliers: 12% (moderately inflated)
                                                                                                                       
benchmarked HashMap/alterInsert/Int                          benchmarked HashMap/alterInsert/Int
time                 692.7 μs   (683.1 μs .. 702.6 μs)       time                 710.3 μs   (703.2 μs .. 717.6 μs)
                     0.999 R²   (0.998 R² .. 0.999 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 664.6 μs   (658.8 μs .. 670.0 μs)       mean                 688.6 μs   (683.9 μs .. 693.2 μs)
std dev              18.04 μs   (14.91 μs .. 22.04 μs)       std dev              15.84 μs   (13.23 μs .. 18.78 μs)
variance introduced by outliers: 12% (moderately inflated)                                                             
                                                             
benchmarked HashMap/alterFInsert/String                      benchmarked HashMap/alterFInsert/String
time                 954.0 μs   (937.1 μs .. 972.8 μs)       time                 936.5 μs   (918.2 μs .. 958.6 μs)
                     0.997 R²   (0.995 R² .. 0.998 R²)                            0.997 R²   (0.996 R² .. 0.999 R²)
mean                 943.7 μs   (933.3 μs .. 954.4 μs)       mean                 929.0 μs   (918.0 μs .. 941.3 μs)
std dev              35.53 μs   (28.87 μs .. 43.77 μs)       std dev              36.52 μs   (30.43 μs .. 44.84 μs)
variance introduced by outliers: 19% (moderately inflated)   variance introduced by outliers: 19% (moderately inflated)
                                                                                                                       
benchmarked HashMap/alterFInsert/ByteString                  benchmarked HashMap/alterFInsert/ByteString
time                 1.022 ms   (1.012 ms .. 1.032 ms)       time                 1.028 ms   (1.020 ms .. 1.037 ms)
                     0.999 R²   (0.998 R² .. 0.999 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 983.2 μs   (973.2 μs .. 991.5 μs)       mean                 1.002 ms   (993.9 μs .. 1.009 ms)
std dev              31.13 μs   (25.74 μs .. 37.93 μs)       std dev              26.09 μs   (21.19 μs .. 31.90 μs)
variance introduced by outliers: 14% (moderately inflated)   variance introduced by outliers: 11% (moderately inflated)
                                                                                                                       
benchmarked HashMap/alterFInsert/Int                         benchmarked HashMap/alterFInsert/Int
time                 674.6 μs   (667.2 μs .. 681.5 μs)       time                 686.7 μs   (674.3 μs .. 698.5 μs)
                     0.999 R²   (0.999 R² .. 0.999 R²)                            0.998 R²   (0.998 R² .. 0.999 R²)
mean                 671.9 μs   (667.0 μs .. 678.7 μs)       mean                 672.0 μs   (666.5 μs .. 676.9 μs)
std dev              19.24 μs   (14.47 μs .. 26.39 μs)       std dev              18.14 μs   (14.43 μs .. 23.45 μs)
variance introduced by outliers: 11% (moderately inflated)   variance introduced by outliers: 11% (moderately inflated)
                                                                                                                       
benchmarked HashMap/alterInsert-dup/String                   benchmarked HashMap/alterInsert-dup/String
time                 543.4 μs   (541.8 μs .. 544.6 μs)       time                 561.9 μs   (559.5 μs .. 565.2 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 547.1 μs   (546.1 μs .. 550.1 μs)       mean                 565.4 μs   (564.6 μs .. 566.5 μs)
std dev              5.076 μs   (1.960 μs .. 9.994 μs)       std dev              2.996 μs   (2.467 μs .. 3.854 μs)
                                                                                                                       
benchmarked HashMap/alterInsert-dup/ByteString               benchmarked HashMap/alterInsert-dup/ByteString
time                 254.5 μs   (254.0 μs .. 255.1 μs)       time                 276.0 μs   (272.6 μs .. 280.5 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            0.999 R²   (0.998 R² .. 1.000 R²)
mean                 256.4 μs   (256.1 μs .. 256.9 μs)       mean                 274.5 μs   (273.5 μs .. 276.2 μs)
std dev              1.303 μs   (1.025 μs .. 1.930 μs)       std dev              4.247 μs   (2.855 μs .. 6.693 μs)
                                                                                                                       
benchmarked HashMap/alterInsert-dup/Int                      benchmarked HashMap/alterInsert-dup/Int
time                 278.8 μs   (277.3 μs .. 279.7 μs)       time                 316.3 μs   (315.4 μs .. 317.5 μs)
                     0.999 R²   (0.995 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 280.6 μs   (279.5 μs .. 284.9 μs)       mean                 317.5 μs   (317.0 μs .. 318.5 μs)
std dev              6.491 μs   (958.6 ns .. 13.53 μs)       std dev              2.059 μs   (1.154 μs .. 3.718 μs)
                                                                                                                       
benchmarked HashMap/alterFInsert-dup/String                  benchmarked HashMap/alterFInsert-dup/String
time                 543.6 μs   (541.2 μs .. 546.9 μs)       time                 550.6 μs   (548.6 μs .. 552.5 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 551.7 μs   (549.9 μs .. 553.9 μs)       mean                 553.3 μs   (552.6 μs .. 555.1 μs)
std dev              6.221 μs   (4.645 μs .. 8.069 μs)       std dev              3.673 μs   (1.848 μs .. 7.198 μs)
                                                                                                                       
benchmarked HashMap/alterFInsert-dup/ByteString              benchmarked HashMap/alterFInsert-dup/ByteString
time                 245.3 μs   (243.9 μs .. 246.9 μs)       time                 241.9 μs   (239.1 μs .. 244.2 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            0.999 R²   (0.997 R² .. 1.000 R²)
mean                 244.9 μs   (244.5 μs .. 245.3 μs)       mean                 245.0 μs   (243.5 μs .. 248.3 μs)
std dev              1.340 μs   (1.099 μs .. 1.695 μs)       std dev              7.219 μs   (2.767 μs .. 12.98 μs)
                                                             variance introduced by outliers: 12% (moderately inflated)
benchmarked HashMap/alterFInsert-dup/Int                     benchmarked HashMap/alterFInsert-dup/Int                                                                            
time                 277.9 μs   (275.3 μs .. 281.2 μs)       time                 278.5 μs   (277.5 μs .. 279.7 μs)
                     0.999 R²   (0.999 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 278.7 μs   (277.9 μs .. 280.1 μs)       mean                 280.2 μs   (279.6 μs .. 280.6 μs)
std dev              3.132 μs   (2.022 μs .. 4.641 μs)       std dev              1.629 μs   (1.287 μs .. 2.298 μs)
                                                                                                                       
benchmarked HashMap/alterDelete/String                       benchmarked HashMap/alterDelete/String
time                 782.0 μs   (777.9 μs .. 787.2 μs)       time                 821.0 μs   (820.0 μs .. 822.8 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 785.4 μs   (784.3 μs .. 786.6 μs)       mean                 826.8 μs   (825.8 μs .. 828.1 μs)
std dev              3.832 μs   (3.230 μs .. 4.572 μs)       std dev              3.648 μs   (2.927 μs .. 4.741 μs)
                                                                                                                       
benchmarked HashMap/alterDelete/ByteString                   benchmarked HashMap/alterDelete/ByteString
time                 472.7 μs   (471.8 μs .. 474.1 μs)       time                 513.7 μs   (512.2 μs .. 515.4 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 476.0 μs   (475.3 μs .. 476.7 μs)       mean                 516.3 μs   (515.7 μs .. 517.0 μs)
std dev              2.164 μs   (1.819 μs .. 2.704 μs)       std dev              2.226 μs   (1.799 μs .. 3.106 μs)
                                                                                                                       
benchmarked HashMap/alterDelete/Int                          benchmarked HashMap/alterDelete/Int
time                 312.8 μs   (311.7 μs .. 314.1 μs)       time                 349.7 μs   (348.1 μs .. 353.3 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (0.999 R² .. 1.000 R²)
mean                 314.1 μs   (313.7 μs .. 314.5 μs)       mean                 350.9 μs   (350.4 μs .. 352.1 μs)
std dev              1.160 μs   (952.9 ns .. 1.476 μs)       std dev              2.472 μs   (1.356 μs .. 4.940 μs)
                                                                                                                       
benchmarked HashMap/alterFDelete/String                      benchmarked HashMap/alterFDelete/String
time                 777.5 μs   (776.3 μs .. 779.3 μs)       time                 788.7 μs   (780.9 μs .. 794.8 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            0.999 R²   (0.998 R² .. 1.000 R²)
mean                 783.9 μs   (782.8 μs .. 785.1 μs)       mean                 808.3 μs   (803.1 μs .. 821.6 μs)
std dev              3.754 μs   (3.098 μs .. 4.711 μs)       std dev              26.60 μs   (11.58 μs .. 49.29 μs)
                                                             variance introduced by outliers: 14% (moderately inflated)
benchmarked HashMap/alterFDelete/ByteString                                                                            
time                 474.4 μs   (473.8 μs .. 475.2 μs)       benchmarked HashMap/alterFDelete/ByteString
                     1.000 R²   (1.000 R² .. 1.000 R²)       time                 487.4 μs   (484.1 μs .. 490.0 μs)
mean                 475.4 μs   (475.0 μs .. 475.8 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
std dev              1.451 μs   (1.233 μs .. 1.737 μs)       mean                 493.0 μs   (490.3 μs .. 500.8 μs)
                                                             std dev              12.78 μs   (1.526 μs .. 23.19 μs)
benchmarked HashMap/alterFDelete/Int                                                                                   
time                 312.5 μs   (307.8 μs .. 315.8 μs)       benchmarked HashMap/alterFDelete/Int
                     0.999 R²   (0.998 R² .. 1.000 R²)       time                 305.6 μs   (302.3 μs .. 309.2 μs)
mean                 311.3 μs   (309.7 μs .. 315.7 μs)                            0.999 R²   (0.999 R² .. 1.000 R²)
std dev              7.833 μs   (3.357 μs .. 14.17 μs)       mean                 302.1 μs   (301.5 μs .. 303.3 μs)
                                                             std dev              2.668 μs   (1.437 μs .. 4.100 μs)
benchmarked HashMap/alterDelete-miss/String                  benchmarked HashMap/alterDelete-miss/String                                                                         
time                 223.9 μs   (221.3 μs .. 226.5 μs)       time                 231.5 μs   (228.0 μs .. 235.6 μs)
                     0.999 R²   (0.999 R² .. 1.000 R²)                            0.997 R²   (0.992 R² .. 1.000 R²)
mean                 221.7 μs   (220.3 μs .. 223.0 μs)       mean                 233.4 μs   (231.3 μs .. 237.9 μs)
std dev              4.282 μs   (3.513 μs .. 5.238 μs)       std dev              9.251 μs   (3.721 μs .. 16.82 μs)
                                                             variance introduced by outliers: 19% (moderately inflated)
benchmarked HashMap/alterDelete-miss/ByteString                                                                        
time                 149.4 μs   (147.5 μs .. 151.8 μs)       benchmarked HashMap/alterDelete-miss/ByteString
                     0.999 R²   (0.999 R² .. 1.000 R²)       time                 165.8 μs   (165.1 μs .. 166.5 μs)
mean                 148.6 μs   (148.1 μs .. 150.1 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
std dev              2.235 μs   (1.169 μs .. 4.437 μs)       mean                 165.6 μs   (165.3 μs .. 165.8 μs)
                                                             std dev              827.7 ns   (712.1 ns .. 978.3 ns)
benchmarked HashMap/alterDelete-miss/Int                                                                               
time                 211.2 μs   (209.9 μs .. 212.5 μs)       benchmarked HashMap/alterDelete-miss/Int
                     1.000 R²   (0.999 R² .. 1.000 R²)       time                 227.0 μs   (224.6 μs .. 228.8 μs)
mean                 215.1 μs   (214.0 μs .. 217.5 μs)                            0.999 R²   (0.998 R² .. 1.000 R²)
std dev              4.958 μs   (2.993 μs .. 8.057 μs)       mean                 225.6 μs   (223.9 μs .. 226.6 μs)
                                                             std dev              4.281 μs   (2.784 μs .. 5.685 μs)
benchmarked HashMap/alterFDelete-miss/String                                                                           
time                 222.2 μs   (220.7 μs .. 224.1 μs)       benchmarked HashMap/alterFDelete-miss/String
                     1.000 R²   (0.999 R² .. 1.000 R²)       time                 219.3 μs   (217.9 μs .. 220.2 μs)
mean                 221.9 μs   (221.2 μs .. 222.8 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
std dev              2.634 μs   (1.995 μs .. 3.549 μs)       mean                 220.4 μs   (219.9 μs .. 222.7 μs)
                                                             std dev              3.151 μs   (785.8 ns .. 7.054 μs)
benchmarked HashMap/alterFDelete-miss/ByteString                                                                       
time                 148.3 μs   (147.1 μs .. 149.3 μs)       benchmarked HashMap/alterFDelete-miss/ByteString
                     1.000 R²   (0.999 R² .. 1.000 R²)       time                 148.7 μs   (147.6 μs .. 149.9 μs)
mean                 148.1 μs   (147.6 μs .. 149.0 μs)                            0.999 R²   (0.999 R² .. 1.000 R²)
std dev              1.975 μs   (1.308 μs .. 3.317 μs)       mean                 149.6 μs   (149.2 μs .. 150.1 μs)
                                                             std dev              1.472 μs   (919.3 ns .. 2.412 μs)
benchmarked HashMap/alterFDelete-miss/Int                    benchmarked HashMap/alterFDelete-miss/Int                                                                           
time                 213.5 μs   (209.9 μs .. 217.1 μs)       time                 207.2 μs   (206.1 μs .. 208.4 μs)
                     0.997 R²   (0.994 R² .. 0.999 R²)                            0.999 R²   (0.998 R² .. 1.000 R²)
mean                 206.7 μs   (204.5 μs .. 208.5 μs)       mean                 205.5 μs   (203.6 μs .. 206.6 μs)
std dev              6.291 μs   (4.408 μs .. 8.921 μs)       std dev              4.472 μs   (2.540 μs .. 6.520 μs)
variance introduced by outliers: 12% (moderately inflated)                                                             
                                                             
benchmarked HashMap/isSubmapOf/String                        benchmarked HashMap/isSubmapOf/String
time                 197.2 μs   (194.6 μs .. 200.0 μs)       time                 193.9 μs   (193.5 μs .. 194.3 μs)
                     0.999 R²   (0.998 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 197.5 μs   (196.7 μs .. 198.6 μs)       mean                 194.2 μs   (194.1 μs .. 194.4 μs)
std dev              2.871 μs   (2.281 μs .. 3.702 μs)       std dev              590.8 ns   (492.7 ns .. 707.2 ns)
                                                                                                                       
benchmarked HashMap/isSubmapOf/ByteString                    benchmarked HashMap/isSubmapOf/ByteString
time                 101.5 μs   (100.7 μs .. 102.0 μs)       time                 101.5 μs   (100.9 μs .. 101.9 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 100.4 μs   (100.1 μs .. 100.6 μs)       mean                 101.3 μs   (101.1 μs .. 101.4 μs)
std dev              795.8 ns   (649.8 ns .. 945.9 ns)       std dev              388.1 ns   (314.7 ns .. 495.0 ns)                                                              
                                                             
benchmarked HashMap/isSubmapOf/Int                           benchmarked HashMap/isSubmapOf/Int
time                 320.1 ns   (295.9 ns .. 339.9 ns)       time                 103.2 ns   (101.6 ns .. 105.7 ns)
                     0.984 R²   (0.980 R² .. 0.997 R²)                            0.999 R²   (0.997 R² .. 1.000 R²)
mean                 314.3 ns   (309.7 ns .. 321.1 ns)       mean                 103.4 ns   (103.0 ns .. 104.1 ns)
std dev              16.01 ns   (11.97 ns .. 19.35 ns)       std dev              1.507 ns   (681.8 ps .. 2.326 ns)
variance introduced by outliers: 24% (moderately inflated)                                                             
                                                             
benchmarked HashMap/isSubmapOfNaive/String                   benchmarked HashMap/isSubmapOfNaive/String
time                 256.6 μs   (255.7 μs .. 257.8 μs)       time                 254.1 μs   (252.4 μs .. 256.2 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (0.999 R² .. 1.000 R²)
mean                 257.6 μs   (257.1 μs .. 258.1 μs)       mean                 255.8 μs   (255.0 μs .. 257.7 μs)
std dev              1.560 μs   (1.272 μs .. 1.893 μs)       std dev              3.930 μs   (2.061 μs .. 7.671 μs)
                                                                                                                       
benchmarked HashMap/isSubmapOfNaive/ByteString               benchmarked HashMap/isSubmapOfNaive/ByteString
time                 97.11 μs   (95.45 μs .. 98.79 μs)       time                 95.21 μs   (94.95 μs .. 95.49 μs)
                     0.999 R²   (0.998 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 96.82 μs   (96.44 μs .. 97.38 μs)       mean                 95.61 μs   (95.49 μs .. 95.79 μs)
std dev              1.529 μs   (1.184 μs .. 2.199 μs)       std dev              440.0 ns   (353.3 ns .. 658.0 ns)
                                                                                                                       
benchmarked HashMap/isSubmapOfNaive/Int                      benchmarked HashMap/isSubmapOfNaive/Int
time                 227.9 ns   (225.9 ns .. 231.0 ns)       time                 51.47 ns   (51.17 ns .. 51.82 ns)
                     0.999 R²   (0.999 R² .. 1.000 R²)                            1.000 R²   (0.999 R² .. 1.000 R²)
mean                 227.8 ns   (227.3 ns .. 228.6 ns)       mean                 51.55 ns   (51.43 ns .. 51.79 ns)
std dev              1.879 ns   (1.372 ns .. 2.648 ns)       std dev              427.2 ps   (258.2 ps .. 654.2 ps)
                                                                                                                       
benchmarked HashMap/union                                    benchmarked HashMap/union
time                 70.21 μs   (70.08 μs .. 70.33 μs)       time                 73.11 μs   (73.00 μs .. 73.28 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 70.17 μs   (70.11 μs .. 70.23 μs)       mean                 73.37 μs   (73.32 μs .. 73.43 μs)
std dev              196.0 ns   (162.2 ns .. 246.0 ns)       std dev              168.5 ns   (141.2 ns .. 212.5 ns)
                                                                                                                       
benchmarked HashMap/map                                      benchmarked HashMap/map
time                 50.26 μs   (49.58 μs .. 51.39 μs)       time                 55.09 μs   (54.67 μs .. 55.35 μs)
                     0.999 R²   (0.998 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 50.96 μs   (50.77 μs .. 51.14 μs)       mean                 54.78 μs   (54.65 μs .. 54.94 μs)
std dev              578.4 ns   (430.9 ns .. 848.3 ns)       std dev              471.7 ns   (375.6 ns .. 585.8 ns)
                                                                                                                       
benchmarked HashMap/difference                               benchmarked HashMap/difference
time                 176.4 μs   (175.0 μs .. 178.8 μs)       time                 179.7 μs   (177.7 μs .. 182.5 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)                            0.999 R²   (0.998 R² .. 1.000 R²)
mean                 175.9 μs   (175.7 μs .. 176.4 μs)       mean                 179.3 μs   (178.8 μs .. 180.9 μs)
std dev              917.5 ns   (439.7 ns .. 1.762 μs)       std dev              2.661 μs   (1.323 μs .. 4.704 μs)
                                                                                                                       
benchmarked HashMap/intersection                             benchmarked HashMap/intersection
time                 184.1 μs   (183.6 μs .. 184.8 μs)       time                 184.9 μs   (182.0 μs .. 190.5 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)                            0.995 R²   (0.989 R² .. 0.999 R²)
mean                 183.8 μs   (183.6 μs .. 184.1 μs)       mean                 182.0 μs   (180.6 μs .. 184.5 μs)
std dev              849.7 ns   (675.1 ns .. 1.113 μs)       std dev              5.687 μs   (3.339 μs .. 8.838 μs)
                                                             variance introduced by outliers: 12% (moderately inflated)
benchmarked HashMap/foldl'                                                                                             
time                 15.84 μs   (15.64 μs .. 16.03 μs)       benchmarked HashMap/foldl'
                     0.999 R²   (0.999 R² .. 1.000 R²)       time                 17.80 μs   (17.32 μs .. 18.55 μs)
mean                 16.02 μs   (15.94 μs .. 16.24 μs)                            0.996 R²   (0.992 R² .. 0.999 R²)
std dev              385.5 ns   (171.2 ns .. 800.5 ns)       mean                 17.81 μs   (17.70 μs .. 18.00 μs)
                                                             std dev              422.3 ns   (268.4 ns .. 631.4 ns)
benchmarked HashMap/foldr                                                                                              
time                 31.88 μs   (31.78 μs .. 32.01 μs)       benchmarked HashMap/foldr
                     1.000 R²   (1.000 R² .. 1.000 R²)       time                 31.32 μs   (31.19 μs .. 31.53 μs)
mean                 31.84 μs   (31.81 μs .. 31.88 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
std dev              124.0 ns   (97.36 ns .. 151.0 ns)       mean                 31.35 μs   (31.30 μs .. 31.41 μs)
                                                             std dev              166.0 ns   (133.5 ns .. 209.4 ns)
benchmarked HashMap/filter                                                                                             
time                 56.76 μs   (56.55 μs .. 56.91 μs)       benchmarked HashMap/filter
                     1.000 R²   (1.000 R² .. 1.000 R²)       time                 53.09 μs   (52.78 μs .. 53.41 μs)
mean                 56.88 μs   (56.78 μs .. 57.02 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
std dev              362.1 ns   (252.7 ns .. 596.0 ns)       mean                 53.34 μs   (53.22 μs .. 53.51 μs)
                                                             std dev              442.5 ns   (335.4 ns .. 569.6 ns)
benchmarked HashMap/filterWithKey                                                                                      
time                 43.46 μs   (43.11 μs .. 43.74 μs)       benchmarked HashMap/filterWithKey
                     1.000 R²   (1.000 R² .. 1.000 R²)       time                 41.50 μs   (41.38 μs .. 41.66 μs)
mean                 43.19 μs   (43.11 μs .. 43.28 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
std dev              274.5 ns   (230.7 ns .. 352.9 ns)       mean                 41.79 μs   (41.71 μs .. 41.91 μs)
                                                             std dev              322.7 ns   (246.3 ns .. 417.8 ns)
benchmarked HashMap/size/String                                                                                        
time                 21.37 μs   (21.05 μs .. 21.61 μs)       benchmarked HashMap/size/String
                     0.999 R²   (0.998 R² .. 1.000 R²)       time                 20.61 μs   (20.52 μs .. 20.72 μs)
mean                 21.68 μs   (21.52 μs .. 22.13 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
std dev              839.1 ns   (282.9 ns .. 1.683 μs)       mean                 20.56 μs   (20.52 μs .. 20.61 μs)
variance introduced by outliers: 17% (moderately inflated)   std dev              137.8 ns   (99.43 ns .. 202.4 ns)
                                                                                                                       
benchmarked HashMap/size/ByteString                          benchmarked HashMap/size/ByteString
time                 21.32 μs   (20.99 μs .. 21.55 μs)       time                 20.93 μs   (20.68 μs .. 21.10 μs)
                     0.999 R²   (0.998 R² .. 0.999 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 21.30 μs   (21.19 μs .. 21.42 μs)       mean                 20.97 μs   (20.88 μs .. 21.07 μs)
std dev              365.3 ns   (309.7 ns .. 441.3 ns)       std dev              280.3 ns   (238.8 ns .. 350.0 ns)
                                                                                                                       
benchmarked HashMap/size/Int                                 benchmarked HashMap/size/Int
time                 13.51 μs   (13.30 μs .. 13.71 μs)       time                 13.44 μs   (13.26 μs .. 13.65 μs)
                     0.999 R²   (0.999 R² .. 1.000 R²)                            0.999 R²   (0.999 R² .. 1.000 R²)
mean                 13.38 μs   (13.34 μs .. 13.44 μs)       mean                 13.40 μs   (13.36 μs .. 13.53 μs)
std dev              168.6 ns   (133.1 ns .. 205.4 ns)       std dev              217.9 ns   (65.60 ns .. 421.3 ns)
                                                                                                                       
benchmarked HashMap/fromList/long/String                     benchmarked HashMap/fromList/long/String
time                 405.9 μs   (403.3 μs .. 408.1 μs)       time                 408.0 μs   (406.5 μs .. 410.4 μs)
                     1.000 R²   (0.999 R² .. 1.000 R²)                            1.000 R²   (1.000 R² .. 1.000 R²)
mean                 419.2 μs   (416.3 μs .. 423.6 μs)       mean                 409.8 μs   (409.0 μs .. 411.2 μs)
std dev              11.44 μs   (8.581 μs .. 16.81 μs)       std dev              3.288 μs   (2.159 μs .. 4.944 μs)
variance introduced by outliers: 11% (moderately inflated)                                                             
                                                             benchmarked HashMap/fromList/long/ByteString
benchmarked HashMap/fromList/long/ByteString                 time                 388.9 μs   (387.9 μs .. 389.6 μs)
time                 394.0 μs   (387.5 μs .. 399.1 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
                     0.999 R²   (0.998 R² .. 1.000 R²)       mean                 392.6 μs   (391.7 μs .. 393.6 μs)
mean                 389.2 μs   (387.7 μs .. 391.4 μs)       std dev              3.067 μs   (2.500 μs .. 3.780 μs)
std dev              6.096 μs   (5.052 μs .. 7.456 μs)                                                                 
                                                             benchmarked HashMap/fromList/long/Int
benchmarked HashMap/fromList/long/Int                        time                 255.7 μs   (254.6 μs .. 257.5 μs)
time                 251.3 μs   (249.3 μs .. 253.8 μs)                            1.000 R²   (0.999 R² .. 1.000 R²)
                     0.999 R²   (0.996 R² .. 1.000 R²)       mean                 255.4 μs   (255.1 μs .. 256.5 μs)
mean                 258.2 μs   (256.9 μs .. 260.4 μs)       std dev              1.609 μs   (743.3 ns .. 3.324 μs)
std dev              5.538 μs   (3.322 μs .. 8.726 μs)                                                                 
                                                             benchmarked HashMap/fromList/short/String
benchmarked HashMap/fromList/short/String                    time                 260.9 μs   (260.4 μs .. 261.4 μs)
time                 250.2 μs   (249.3 μs .. 251.5 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
                     1.000 R²   (1.000 R² .. 1.000 R²)       mean                 262.3 μs   (262.1 μs .. 262.7 μs)
mean                 252.0 μs   (251.5 μs .. 253.6 μs)       std dev              923.3 ns   (724.6 ns .. 1.251 μs)
std dev              2.592 μs   (1.059 μs .. 5.086 μs)                                                                 
                                                             benchmarked HashMap/fromList/short/ByteString
benchmarked HashMap/fromList/short/ByteString                time                 250.8 μs   (250.0 μs .. 251.5 μs)
time                 235.7 μs   (233.8 μs .. 237.6 μs)                            0.999 R²   (0.996 R² .. 1.000 R²)
                     0.999 R²   (0.998 R² .. 1.000 R²)       mean                 252.3 μs   (251.5 μs .. 255.2 μs)
mean                 233.0 μs   (232.1 μs .. 234.5 μs)       std dev              4.786 μs   (561.9 ns .. 10.11 μs)
std dev              3.606 μs   (2.063 μs .. 6.254 μs)                                                                 
                                                             benchmarked HashMap/fromList/short/Int
benchmarked HashMap/fromList/short/Int                       time                 163.5 μs   (163.1 μs .. 163.9 μs)
time                 163.7 μs   (161.9 μs .. 166.2 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
                     0.999 R²   (0.999 R² .. 1.000 R²)       mean                 164.3 μs   (164.1 μs .. 164.5 μs)
mean                 165.4 μs   (164.7 μs .. 166.1 μs)       std dev              663.9 ns   (527.8 ns .. 853.5 ns)
std dev              2.409 μs   (1.775 μs .. 3.331 μs)                                                                 
                                                             benchmarked HashMap/fromListWith/long/String
benchmarked HashMap/fromListWith/long/String                 time                 431.1 μs   (430.6 μs .. 431.6 μs)
time                 423.9 μs   (418.8 μs .. 428.4 μs)                            1.000 R²   (1.000 R² .. 1.000 R²)
                     0.999 R²   (0.998 R² .. 1.000 R²)       mean                 433.6 μs   (433.1 μs .. 434.5 μs)
mean                 437.6 μs   (433.9 μs .. 447.6 μs)       std dev              2.062 μs   (1.336 μs .. 3.650 μs)
std dev              18.12 μs   (8.544 μs .. 35.72 μs)                                                                 
variance introduced by outliers: 23% (moderately inflated)   benchmarked HashMap/fromListWith/long/ByteString
                                                             time                 404.3 μs   (403.4 μs .. 405.0 μs)
benchmarked HashMap/fromListWith/long/ByteString                                  1.000 R²   (1.000 R² .. 1.000 R²)
time                 396.7 μs   (394.4 μs .. 400.1 μs)       mean                 405.3 μs   (404.8 μs .. 405.9 μs)
                     0.999 R²   (0.998 R² .. 1.000 R²)       std dev              1.744 μs   (1.309 μs .. 2.714 μs)
mean                 399.3 μs   (398.4 μs .. 400.9 μs)                                                                 
std dev              3.942 μs   (2.305 μs .. 7.098 μs)       benchmarked HashMap/fromListWith/long/Int
                                                             time                 283.3 μs   (282.7 μs .. 283.9 μs)
benchmarked HashMap/fromListWith/long/Int                                         1.000 R²   (1.000 R² .. 1.000 R²)
time                 280.7 μs   (278.4 μs .. 282.6 μs)       mean                 285.1 μs   (284.7 μs .. 285.6 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       std dev              1.277 μs   (1.083 μs .. 1.576 μs)
mean                 278.9 μs   (278.6 μs .. 279.4 μs)                                                                 
std dev              1.441 μs   (1.022 μs .. 1.998 μs)       benchmarked HashMap/fromListWith/short/String
                                                             time                 279.2 μs   (278.0 μs .. 280.6 μs)
benchmarked HashMap/fromListWith/short/String                                     1.000 R²   (1.000 R² .. 1.000 R²)
time                 260.6 μs   (259.4 μs .. 261.7 μs)       mean                 278.3 μs   (277.9 μs .. 279.0 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       std dev              1.752 μs   (1.247 μs .. 2.414 μs)
mean                 260.1 μs   (259.6 μs .. 260.8 μs)                                                                 
std dev              1.713 μs   (1.311 μs .. 2.396 μs)       benchmarked HashMap/fromListWith/short/ByteString
                                                             time                 275.9 μs   (271.9 μs .. 280.2 μs)
benchmarked HashMap/fromListWith/short/ByteString                                 0.999 R²   (0.998 R² .. 0.999 R²)
time                 249.9 μs   (249.2 μs .. 250.7 μs)       mean                 273.1 μs   (272.2 μs .. 274.4 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       std dev              3.734 μs   (2.503 μs .. 5.160 μs)
mean                 252.3 μs   (251.8 μs .. 252.9 μs)                                                                 
std dev              1.809 μs   (1.212 μs .. 2.694 μs)       benchmarked HashMap/fromListWith/short/Int
                                                             time                 178.0 μs   (177.6 μs .. 178.3 μs)
benchmarked HashMap/fromListWith/short/Int                                        1.000 R²   (1.000 R² .. 1.000 R²)
time                 174.6 μs   (173.7 μs .. 175.7 μs)       mean                 178.1 μs   (178.0 μs .. 178.2 μs)
                     1.000 R²   (1.000 R² .. 1.000 R²)       std dev              389.6 ns   (324.0 ns .. 479.9 ns)
mean                 175.0 μs   (174.6 μs .. 175.8 μs)       
std dev              1.712 μs   (730.9 ns .. 2.816 μs)

@jappeace
Copy link
Author

So, If I understand correctly, the boxing issue has little impact on performance.

I kindly invite others to reproduce these results, or perhaps point out mistakes I've made. I'm new to this after all.

@jappeace jappeace force-pushed the add-typelevel-salt branch 4 times, most recently from 7a4681c to 73fa02e Compare September 22, 2021 16:32
This allows clients to create custom salted hashmaps.
For backwards compatibility we use

```haskell
 -- backwards compatibility
type HashMap = HashMapT DefaultSalt
```

Then modify the functions to be free of salt if they can, for example insert:

```haskell
insert :: forall k v salt . (Eq k, Hashable k) => k -> v -> HashMapT salt k v -> HashMapT salt k v
insert k v m = insert' (hash salt k) k v m
  where
    salt = natVal (Proxy :: Proxy salt)
```

This allows the default HashMap with backwards compatibility,
but also any other HashMapT
I think this solves the issue with having different salts in
an intersect:

```haskell
intersection :: (Eq k, Hashable k) => HashMapT salt k v -> HashMapT salt k w -> HashMapT salt k v
```

Because salt is the same type variable for all arguments,
it's enforced to be the same.
Then you can also provide a function to resalt if the user ever ends
up with different salts and still wants to do an intersect.
(which results in a reconstruction of the hashmap).

See thread: haskell-unordered-containers#319

I know these libraries will at least fail with the changes because they have instances on `HashMap`,
which now need to be `HashMapT salt`:

quickcheck-instances-0.3.25.2.drv
semirings-0.6.drv
relude-0.7.0.0.drv
semigroupoids-5.3.5.drv

I did this by stubbing out unordered containers in a 100k loc codebase
to see what issues would come up in CI.
@jappeace jappeace changed the title Add typelevel salt Add HashMapT salt, which allows creation of salt with Nat. Sep 22, 2021
@jappeace jappeace changed the title Add HashMapT salt, which allows creation of salt with Nat. Add HashMapT salt, which allows setting of salt with Nat. Sep 22, 2021
@jappeace jappeace marked this pull request as ready for review September 22, 2021 16:37
@treeowl
Copy link
Collaborator

treeowl commented Sep 22, 2021

We could easily fix the compatibility issues by making HashMap a newtype rather than a type synonym. Is there a reason not to do that?

Copy link
Collaborator

@treeowl treeowl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't reviewed the whole patch, but I hope you'll find some of my suggestions useful. I'm still not too clear on the purpose of this whole exercise. It still won't lead to a secure implementation; is it solely for the purpose of preventing accidentally repeated bad performance? If so, is it really worth the complexity?

| Collision !Hash !(A.Array (Leaf k v))
deriving (Typeable)

type role HashMap nominal representational
-- backwards compatibility
type HashMap = HashMapT DefaultSalt
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me a bit nervous. Beginners might be a bit frightened by HashMap being a type synonym for some wacky type-tagged thing.

@@ -196,8 +205,10 @@ import Data.Coerce (coerce)
------------------------------------------------------------------------

-- | Convenience function. Compute a hash value for the given value.
hash :: H.Hashable a => a -> Hash
hash = fromIntegral . H.hash
hash ::forall a (salt :: Nat) . H.Hashable a => KnownNat salt => Proxy salt -> a -> Hash
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for the double => rather than the conventional constraint tuple? If so, please add an explanatory comment. Also, is there a reason you use Proxy here and not Proxy#? I'd prefer the latter to be more confident we won't pass an actual run-time value.

hash ::forall a (salt :: Nat) . H.Hashable a => KnownNat salt => Proxy salt -> a -> Hash
hash proxy = fromIntegral . H.hashWithSalt (fromIntegral salt)
where
salt = natVal proxy -- TODO ensure is unboxed, ensure is a Word
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I very much doubt that it's possible to unbox it with current GHC, but I'd love to be proven wrong.

Question: would we get better code/specialization/performance with an auxiliary class? I'm not at all sure, but it seems vaguely plausible.

wordVal :: forall n. KnownWord n => Proxy# n -> Word
wordVal _ = unTagged (wordVal' :: Tagged n Word)

class KnownWord (n :: Nat) where
  wordVal' :: Tagged n Word
instance KnownNat n => KnownWord n where
#if MIN_VERSION_base(4,8,0)
  wordVal = Tagged $ fromIntegral (natVal' (proxy# :: Proxy# n))
#else
  wordVal _ = Tagged $ fromIntegral (natVal (Proxy :: Proxy n))
#endif

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused about this, how do I know if the resulting code boxes it or not?
My understanding of boxing is in C:

int* salt = malloc(sizeof(int)); // reserves memory somehwere, pointer gets pushed on stack
(*salt) = 50; // put 50 into memory somehwere using pointer

rather then

int salt = 50; // put on stack

How do I make ghc tell me which of those two options it chooses?


-- these come from
-- https://github.com/haskell-unordered-containers/hashable/blob/master/src/Data/Hashable/Class.hs#L221
-- Can't have negatives in a natural.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just don't use a Natural.

type DefaultSalt = 14695981039346656037 -- fromInteger (-3750763034362895579) :: Word64
#else
type DefaultSalt = 15868100553162883236 -- old values https://github.com/haskell-unordered-containers/hashable/blame/ade7f97d1c59e9cfbad49b6ed130b90805311758/Data/Hashable/Class.hs#L202
#endif
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you expand these comments a bit to add context?

empty = empty'

-- | like 'empty' but allows a custom salt to be set
empty' :: forall k v (salt :: Nat) . HashMapT salt k v
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the salt should be the first type argument for use with TypeApplications.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, for new API's I can do this, for old API's I need to retain push the salt to the final position (in case someone used type applications).

HashMap is now an alias to HashMapT with a hardcoded DefaultSalt.
These changes allow salt to be anything.
semigroup operations (a -> a -> a) can use the same salt to guarantee
not having to rebuild the hahsmap.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
not having to rebuild the hahsmap.
not having to rebuild the hashmap.

usually it's good enough to provide the instance with a free salt:

```haskell
instance SomeTypeClass (HashMap salt k v) where
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't that be HashMapT?

singleton = singleton'

-- | like 'singleton' but allows a custom salt to be set
singleton' :: forall k v (salt :: Nat) . (Hashable k, KnownNat salt) => k -> v -> HashMapT salt k v
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, I think the salt should be the first type argument. That may even always be the case.


-- | /O(1)/ Construct a map with a single element.
singleton :: (Hashable k) => k -> v -> HashMap k v
singleton k v = Leaf (hash k) (L k v)
singleton :: forall k v . (Hashable k) => k -> v -> HashMap k v
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you're here, remove the redundant parentheses.

@jappeace
Copy link
Author

We could easily fix the compatibility issues by making HashMap a newtype rather than a type synonym. Is there a reason not to do that?

Good question.

It'd solve the instance head problem, but introduce another problem for existing functions,

consider empty and insert:

empty :: forall k v . HashMap k v
insert :: forall k v salt . (Eq k, Hashable k, KnownNat salt) => k -> v -> HashMapT salt k v -> HashMapT salt k v

The empty no longer fits in insert because of the newtype HashMap,
We could work around this as well:

insert :: forall k v salt . (Eq k, Hashable k, KnownNat salt, Coercable (map k v) (HashMapT salt k v) , Coercable (HashMapT salt k v) (map k v)  => k -> v -> map k v -> map k v
insert k v m = coerce $ ...
  where
   orig :: HashMapT salt k v
   orig = coerce m 

I think that option has to be weighed against the cost of fixing downstream instance heads, which (mostly (1) ) can be solved with a find and replace.
Note that if we provide the newtype compatibility, it may still be the case maintainers have to fix their instance heads because it's used in functions of the typeclass, which would break with a custom salt.
Relude for example would want to solve this issue:

(1): I had to introduce an additional typeable constraint for Data.Data in older ghc < 8 versions.


I'll try fixing review comments after work.

@sjakobi
Copy link
Member

sjakobi commented Sep 23, 2021

Thanks for giving us a concrete implementation to play with, @jappeace! :)

Before I dive into the implementation, I'd first like to discuss the basic design and how it helps address #319.

In particular, I'm wondering how this design compares to a more simplistic approach to #45, like

data Hashmap = HashMap
  { hm :: !HashMap' -- the old HashMap type
  , salt :: !Int
  }

I'm also not convinced yet that we need to have different hash salts per HashMap. AFAIU the salt needs to be unpredictable for the attacker, which I think we can also achieve with something like hashable's -frandom-init-seed flag.

Does one of these approaches make it more difficult to leak a secret salt? I'm not sure!

My suggestion would be to discuss these design issues separately from this PR, in a new issue.

@jappeace
Copy link
Author

I have to answer all these questions later (after work), because it involves some verifying of my intuition, but you're right this is just a possible solution (don't mean to pressure anyone). I just saw treeowl also has some more fundemental questions.

@treeowl
Copy link
Collaborator

treeowl commented Sep 23, 2021

Without suggesting that any of this is ultimately right for the library, the newtype approach would just have to go much further. No more primed and unprimed functions, but rather a whole Salted module holding the salty versions and the current module with wrappers. So Salted.insert would have a salty type and Lazy.insert would deal with newtypes. @sjakobi , the point of the type level business would be to deal with merging operations (unions, intersections, and differences). Those don't go so well with a non-typey stash-the-salt approach.

@jappeace
Copy link
Author

is it solely for the purpose of preventing accidentally repeated bad performance? If so, is it really worth the complexity?

It'll give the user the capability to use a different salt, per hashmap value.
Let's say I generate a random salt per request before ingesting json, then I use that to build a continuation to use it:

  getRngSalt <- randomIO
  case someNatVal getRngSalt of
        (Just (SomeNat (proxy :: Proxy n))) ->
          print (HML.singleton' @_ @_ @n "hi" 0 ) -- here we can use n with a random value.
        Nothing -> pure ()

This won't make HashMap perse more robust against collision attacks,
but it'll allow users to make it as secure as they want.
If they don't care about security the default salt is good. but if they do they can do a slightly more difficult approach of setting their own salt, or even better the approach I showed above. *
Setting your own salt at typelevel will be better, but not secure because FNV isn't designed around hiding a salt, like SipHash is.

But, if the salt can be changed it starts making more sense to look at SipHash because we now can hide the k, which I got from this passage on wikipedia:

. SipHash instead guarantees that, having seen Xi and SipHash(Xi, k), an attacker who does not know the key k cannot find (any information about) k or SipHash(Y, k) for any message Y ∉ {Xi} which they have not seen before.

But I really need to read the paper to verify this claim.

  • I suspect that the approach I showed above could have an impact on the benchmarks so I want to test it as well, it no longer allows ghc so see the given Nat as a constant.

@jappeace
Copy link
Author

@sjakobi

I think treeowl already did a good job of explaining, but I'll expand it with some examples.

Consider union

union :: (Eq k, Hashable k, KnownNat salt) => 
              HashMap k v -- salt of 5
         ->  HashMap k v -- salt of 6
         -> HashMap k v

now the hashable instance will produce misaligning hashes for the same value. For example a key "hello world" would be:

hashWithSalt 5 "hello world" = 2407350307841482486
hashWithSalt 6 "hello world" = -5301410815273744343

The implementation however doesn't do a full reconstruct, instead it aligns the datastructures, for example:

    -- empty vs. anything
    go !_ t1 Empty = t1
    go _ Empty t2 = t2
    -- leaf vs. leaf
    go s t1@(Leaf h1 l1@(L k1 v1)) t2@(Leaf h2 l2@(L k2 v2))
        | h1 == h2  = if k1 == k2
                      then Leaf h1 (L k1 (f k1 v1 v2))
                      else collision h1 l1 l2
        | otherwise = goDifferentHash s h1 h2 t1 t2

If we'd do a simpler approach we'd have to always reconstruct one of them on unequal salts (which is slow).

Another alternative is to tell the user the salts don't allign:

data UnionErrors = SaltsNoAlign Int Int
union :: (Eq k, Hashable k, KnownNat salt) => HashMapT salt k v -> HashMapT salt k v -> Either UnionErrors (HashMapT salt k v)

This breaks the API.


AFAIU the salt needs to be unpredictable for the attacker, which I think we can also achieve with something like hashable's -frandom-init-seed flag.

it would be better, although FNV isn't really resistant against hash flooding attacks.
Python for example replaced it in 2013: https://www.python.org/dev/peps/pep-0456/#id29 (the rationale section).
It'd be practically impossible to obtain the salt if your salt is a random number on every request. (I presume they meant salt when they said those researchers obtained the seed in the pep).

@sjakobi
Copy link
Member

sjakobi commented Sep 27, 2021

@treeowl

the point of the type level business would be to deal with merging operations (unions, intersections, and differences). Those don't go so well with a non-typey stash-the-salt approach.

I understand that the type-level salt ensures that we use the fast non-rehashing algorithms for combining HashMaps. What's the story for combining HashMaps with different salts though?

I think the advantage of a simple "stashed salt" approach is that we could use the same API for both cases. In the implementation, we'd check whether the salts are the same, and, depending on the outcome, use either the fast non-rehashing algorithm or a slower algorithm that involves rehashing the keys of one of the maps.


@jappeace

It'll give the user the capability to use a different salt, per hashmap value.
Let's say I generate a random salt per request before ingesting json, then I use that to build a continuation to use it:

This won't make HashMap perse more robust against collision attacks,
but it'll allow users to make it as secure as they want.
If they don't care about security the default salt is good. but if they do they can do a slightly more difficult approach of setting their own salt, or even better the approach I showed above. *

My understanding is that, as long as FNV is used, even a random seed per request would be pretty useless, since it's possible to generate seed-independent collisions for FNV: https://medium.com/@robertgrosse/generating-64-bit-hash-collisions-to-dos-python-5b21404a5306

Also, since you brought up SipHash: My current perspective is that any approach involving SipHash would be pointless, because SipHash seems to be too slow: #319 (comment). We need a faster hash function.

@jappeace
Copy link
Author

@sjakobi Thanks! That's an excellent blogpost. You've convinced me this by itself isn't enough, even if there is a random salt per request.

I'll keep further discussion in #319

@mikeplus64
Copy link

Using this KnownNat salt approach, it is possible still to generate a usable salt at runtime. You have to use some GHC weirdness though, see reifyNat.

I'd suggest maybe going even further and using DataKinds for the salt type, like

data Salt = Salt Nat | GlobalRandomSalt
type DefaultSalt = 'Salt _

One of the problems of this approach of using KnownNat or any type-level Nat is that if the Nat isn't known by GHC, such as if you ever stored a hashmap like data H = H (forall s. KnownNat s => HashMapT s _ _) getting the Word will always involve a fromInteger call. Same will go for most cases where the salt is generated at runtime AFAIK.

@treeowl
Copy link
Collaborator

treeowl commented Sep 28, 2021

We don't actually need to use Nat for this approach, even if we choose to use this approach at all. KnownWord with reifyWord would do. But I'm pretty sure it would still be boxed. The question remains of whether any of this is a good idea. @sjakobi, your stashed salt approach sounds good until you have a non-unboxed container of HashMaps, at which point there's an extra indirection at the root of each map (through a three-word heap object). I don't think being able to combine maps with different salts implicitly is a real advantage: it's rather expensive, and you have to have some arbitrary (but important to the user) rule for which salt wins. Much better, I think, to rehash explicitly if we do something like this.

@mikeplus64
Copy link

But I'm pretty sure it would still be boxed.

Probably but there are some tricks to cover most cases.

At least under -O2, GHC is pretty good at not boxing staticKnownNat reifications when turning them into another integral type, because it comes down to fromInteger (natVal (_ :: proxy 1234)) :: Int => fromInteger 1234 :: Int which is just how the desugaring for integers works anyway. I don't think it would be any different for KnownWord as the instance just uses KnownNat anyway.

But to really maximise the chances that at every site the salt is reified from type to value, it's represented as a constant Word# in the final code, the caller must be inlined at least up to that point -- something like

wordVal# :: KnownWord w => Proxy# w -> Word#
wordVal# = ...

{-# INLINE insert #-}
insert :: forall salt k v. (KnownWord salt, Eq k, Hashable k) => k -> v -> HashMapT salt k v -> HashMapT salt k v
insert k v m = unsafeInsert# (wordVal# (Proxy# :: Proxy# salt)) k v m

-- whatever original inlinable/inline/noinline goes pragma here
unsafeInsert# :: forall salt k v. (KnownWord salt, Eq k, Hashable k) => Word# -> k -> v -> HashMapT salt k v -> HashMapT salt k v
unsafeInsert# = ...

Which would definitely turn this into a pretty big/ugly refactor. AFAIK wherever the KnownNat/KnownWord dictionary has to actually exist at runtime, the value will be boxed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants