Skip to content

Commit 8c7b2aa

Browse files
committed
Add benchmarks to hashmap operations inside containers
* because 'HashMap' is now a wrapper and may/may not get unboxed during a program's execution, benchmarks to operations on sets of hashmaps inside different kinds of containers were added;
1 parent 1ea9c72 commit 8c7b2aa

File tree

2 files changed

+169
-6
lines changed

2 files changed

+169
-6
lines changed

benchmarks/Benchmarks.hs

+168-6
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ import Data.Hashable (Hashable, hash)
1010
import qualified Data.ByteString as BS
1111
import qualified "hashmap" Data.HashMap as IHM
1212
import qualified Data.HashMap.Strict as HM
13+
import qualified "unordered-containers" Data.HashSet as HS
1314
import qualified Data.IntMap as IM
1415
import qualified Data.Map as M
16+
import qualified Data.Set as S
17+
import qualified Data.Vector as V
1518
import Data.List (foldl')
1619
import Data.Maybe (fromMaybe)
1720
import GHC.Generics (Generic)
@@ -36,6 +39,8 @@ instance NFData B where
3639
data Env = Env {
3740
n :: !Int,
3841

42+
csz :: !Int, -- container size
43+
3944
elems :: ![(String, Int)],
4045
keys :: ![String],
4146
elemsBS :: ![(BS.ByteString, Int)],
@@ -48,6 +53,11 @@ data Env = Env {
4853
keysBS' :: ![BS.ByteString],
4954
keysI' :: ![Int],
5055

56+
listOfHMs :: ![HM.HashMap Int Int],
57+
vecOfHMs :: !(V.Vector (HM.HashMap Int Int)),
58+
hsetOfHMs :: !(HS.HashSet (HM.HashMap Int Int)),
59+
setOfHMs :: !(S.Set (HM.HashMap Int Int)),
60+
5161
keysDup :: ![String],
5262
keysDupBS :: ![BS.ByteString],
5363
keysDupI :: ![Int],
@@ -78,6 +88,20 @@ setupEnv :: IO Env
7888
setupEnv = do
7989
let n = 2^(12 :: Int)
8090

91+
-- When building a container of hashmaps, 'cn' will be the size of each.
92+
cn = n `div` 16
93+
-- 'csz' is the size of the container of hashmaps.
94+
csz = 2^(7 :: Int)
95+
96+
values = [1..csz*cn]
97+
98+
chop _ [] = []
99+
chop k l =
100+
let (taken, left) = splitAt k l
101+
in taken : chop k left
102+
103+
vals = chop cn values
104+
81105
elems = zip keys [1..n]
82106
keys = US.rnd 8 n
83107
elemsBS = zip keysBS [1..n]
@@ -90,6 +114,11 @@ setupEnv = do
90114
keysBS' = UBS.rnd' 8 n
91115
keysI' = UI.rnd' (n+n) n
92116

117+
listOfHMs = zipWith (\x y -> HM.fromList (zip x y)) (repeat keysI) vals
118+
vecOfHMs = V.fromList listOfHMs
119+
hsetOfHMs = HS.fromList listOfHMs
120+
setOfHMs = S.fromList listOfHMs
121+
93122
keysDup = US.rnd 2 n
94123
keysDupBS = UBS.rnd 2 n
95124
keysDupI = UI.rnd (n`div`4) n
@@ -126,8 +155,8 @@ main = do
126155
defaultMain
127156
[
128157
env setupEnv $ \ ~(Env{..}) ->
129-
-- * Comparison to other data structures
130-
-- ** Map
158+
-- Comparison to other data structures
159+
-- Map
131160
bgroup "Map"
132161
[ bgroup "lookup"
133162
[ bench "String" $ whnf (lookupM keys) m
@@ -167,7 +196,7 @@ main = do
167196
]
168197
]
169198

170-
-- ** Map from the hashmap package
199+
-- Map from the hashmap package
171200
, env setupEnv $ \ ~(Env{..}) ->
172201
bgroup "hashmap/Map"
173202
[ bgroup "lookup"
@@ -212,7 +241,7 @@ main = do
212241
]
213242
]
214243

215-
-- ** IntMap
244+
-- IntMap
216245
, env setupEnv $ \ ~(Env{..}) ->
217246
bgroup "IntMap"
218247
[ bench "lookup" $ whnf (lookupIM keysI) im
@@ -228,7 +257,7 @@ main = do
228257

229258
, env setupEnv $ \ ~(Env{..}) ->
230259
bgroup "HashMap"
231-
[ -- * Basic interface
260+
[ -- Basic interface
232261
bgroup "lookup"
233262
[ bench "String" $ whnf (lookup keys) hm
234263
, bench "ByteString" $ whnf (lookup keysBS) hmbs
@@ -310,13 +339,58 @@ main = do
310339
, bench "Int" $ whnf (isSubmapOfNaive hmiSubset) hmi
311340
]
312341

342+
, bgroup "containerized"
343+
[ bgroup "lookup"
344+
[ bench "List" $ nf (lookupC keysI) listOfHMs
345+
, bench "Vector" $ nf (lookupC keysI) vecOfHMs
346+
, bench "HashSet" $ nf (lookupHS keysI) hsetOfHMs
347+
, bench "Set" $ nf (lookupS keysI) setOfHMs
348+
]
349+
, bgroup "insert"
350+
[ bench "List" $ nf (insertC elemsI) listOfHMs
351+
, bench "Vector" $ nf (insertC elemsI) vecOfHMs
352+
, bench "HashSet" $ nf (insertHS elemsI) hsetOfHMs
353+
, bench "Set" $ nf (insertS elemsI) setOfHMs
354+
]
355+
, bgroup "delete"
356+
[ bench "List" $ nf (deleteC keysI) listOfHMs
357+
, bench "Vector" $ nf (deleteC keysI) vecOfHMs
358+
, bench "HashSet" $ nf (deleteHS keysI) hsetOfHMs
359+
, bench "Set" $ nf (deleteS keysI) setOfHMs
360+
]
361+
, bgroup "union"
362+
[ bench "List" $ whnf unionC listOfHMs
363+
, bench "Vector" $ whnf unionC vecOfHMs
364+
, bench "HashSet" $ whnf unionC hsetOfHMs
365+
, bench "Set" $ whnf unionC setOfHMs
366+
]
367+
, bgroup "map"
368+
[ bench "List" $ nf (mapC (\ v -> v + 1)) listOfHMs
369+
, bench "Vector" $ nf (mapC (\ v -> v + 1)) vecOfHMs
370+
, bench "HashSet" $ nf (mapHS (\ v -> v + 1)) hsetOfHMs
371+
, bench "Set" $ nf (mapS (\ v -> v + 1)) setOfHMs
372+
]
373+
, bgroup "intersection"
374+
[ bench "List" $ whnf intersectionC listOfHMs
375+
, bench "Vector" $ whnf intersectionC vecOfHMs
376+
, bench "HashSet" $ whnf intersectionC hsetOfHMs
377+
, bench "Set" $ whnf intersectionC setOfHMs
378+
]
379+
, bgroup "size"
380+
[ bench "List" $ nf sizeC listOfHMs
381+
, bench "Vector" $ nf sizeC vecOfHMs
382+
, bench "HashSet" $ nf sizeHS hsetOfHMs
383+
, bench "Set" $ nf sizeS setOfHMs
384+
]
385+
]
386+
313387
-- Combine
314388
, bench "union" $ whnf (HM.union hmi) hmi2
315389

316390
-- Transformations
317391
, bench "map" $ whnf (HM.map (\ v -> v + 1)) hmi
318392

319-
-- * Difference and intersection
393+
-- Difference and intersection
320394
, bench "difference" $ whnf (HM.difference hmi) hmi2
321395
, bench "intersection" $ whnf (HM.intersection hmi) hmi2
322396

@@ -374,6 +448,18 @@ lookup xs m = foldl' (\z k -> fromMaybe z (HM.lookup k m)) 0 xs
374448
{-# SPECIALIZE lookup :: [BS.ByteString] -> HM.HashMap BS.ByteString Int
375449
-> Int #-}
376450

451+
lookupC :: (Eq k, Hashable k, Traversable f) => [k] -> f (HM.HashMap k Int) -> f Int
452+
lookupC = fmap . lookup
453+
{-# SPECIALIZE lookupC :: [Int] -> [HM.HashMap Int Int] -> [Int] #-}
454+
{-# SPECIALIZE lookupC :: [Int] -> V.Vector (HM.HashMap Int Int)
455+
-> V.Vector Int #-}
456+
457+
lookupHS :: [Int] -> HS.HashSet (HM.HashMap Int Int) -> HS.HashSet Int
458+
lookupHS = HS.map . lookup
459+
460+
lookupS :: [Int] -> S.Set (HM.HashMap Int Int) -> S.Set Int
461+
lookupS = S.map . lookup
462+
377463
insert :: (Eq k, Hashable k) => [(k, Int)] -> HM.HashMap k Int
378464
-> HM.HashMap k Int
379465
insert xs m0 = foldl' (\m (k, v) -> HM.insert k v m) m0 xs
@@ -384,6 +470,21 @@ insert xs m0 = foldl' (\m (k, v) -> HM.insert k v m) m0 xs
384470
{-# SPECIALIZE insert :: [(BS.ByteString, Int)] -> HM.HashMap BS.ByteString Int
385471
-> HM.HashMap BS.ByteString Int #-}
386472

473+
insertC :: (Eq k, Hashable k, Traversable f) => [(k, Int)] -> f (HM.HashMap k Int)
474+
-> f (HM.HashMap k Int)
475+
insertC l = fmap (insert l)
476+
{-# SPECIALIZE insertC :: [(Int, Int)] -> [HM.HashMap Int Int]
477+
-> [HM.HashMap Int Int] #-}
478+
{-# SPECIALIZE insertC :: [(Int, Int)] -> V.Vector (HM.HashMap Int Int)
479+
-> V.Vector (HM.HashMap Int Int) #-}
480+
481+
insertHS :: [(Int, Int)] -> HS.HashSet (HM.HashMap Int Int)
482+
-> HS.HashSet (HM.HashMap Int Int)
483+
insertHS l = HS.map (insert l)
484+
485+
insertS :: [(Int, Int)] -> S.Set (HM.HashMap Int Int) -> S.Set (HM.HashMap Int Int)
486+
insertS l = S.map (insert l)
487+
387488
delete :: (Eq k, Hashable k) => [k] -> HM.HashMap k Int -> HM.HashMap k Int
388489
delete xs m0 = foldl' (\m k -> HM.delete k m) m0 xs
389490
{-# SPECIALIZE delete :: [Int] -> HM.HashMap Int Int -> HM.HashMap Int Int #-}
@@ -392,6 +493,21 @@ delete xs m0 = foldl' (\m k -> HM.delete k m) m0 xs
392493
{-# SPECIALIZE delete :: [BS.ByteString] -> HM.HashMap BS.ByteString Int
393494
-> HM.HashMap BS.ByteString Int #-}
394495

496+
deleteC :: (Eq k, Hashable k, Functor f) => [k] -> f (HM.HashMap k Int)
497+
-> f (HM.HashMap k Int)
498+
deleteC = fmap . delete
499+
{-# SPECIALIZE deleteC :: [Int] -> [HM.HashMap Int Int]
500+
-> [HM.HashMap Int Int] #-}
501+
{-# SPECIALIZE deleteC :: [Int] -> V.Vector (HM.HashMap Int Int)
502+
-> V.Vector (HM.HashMap Int Int) #-}
503+
504+
deleteHS :: [Int] -> HS.HashSet (HM.HashMap Int Int)
505+
-> HS.HashSet (HM.HashMap Int Int)
506+
deleteHS = HS.map . delete
507+
508+
deleteS :: [Int] -> S.Set (HM.HashMap Int Int) -> S.Set (HM.HashMap Int Int)
509+
deleteS = S.map . delete
510+
395511
alterInsert :: (Eq k, Hashable k) => [(k, Int)] -> HM.HashMap k Int
396512
-> HM.HashMap k Int
397513
alterInsert xs m0 =
@@ -436,6 +552,52 @@ alterFDelete xs m0 =
436552
{-# SPECIALIZE alterFDelete :: [BS.ByteString] -> HM.HashMap BS.ByteString Int
437553
-> HM.HashMap BS.ByteString Int #-}
438554

555+
unionC :: (Eq k, Hashable k, Foldable f) => f (HM.HashMap k Int)
556+
-> HM.HashMap k Int
557+
unionC = foldl' HM.union mempty
558+
{-# SPECIALIZE unionC :: [HM.HashMap Int Int] -> HM.HashMap Int Int #-}
559+
{-# SPECIALIZE unionC :: V.Vector (HM.HashMap Int Int) -> HM.HashMap Int Int #-}
560+
{-# SPECIALIZE unionC :: HS.HashSet (HM.HashMap Int Int) -> HM.HashMap Int Int #-}
561+
{-# SPECIALIZE unionC :: S.Set (HM.HashMap Int Int) -> HM.HashMap Int Int #-}
562+
563+
mapC :: (Eq k, Hashable k, Functor f) => (Int -> Int) -> f (HM.HashMap k Int)
564+
-> f (HM.HashMap k Int)
565+
mapC f = fmap (HM.map f)
566+
{-# SPECIALIZE mapC :: (Int -> Int) -> [HM.HashMap Int Int]
567+
-> [HM.HashMap Int Int] #-}
568+
{-# SPECIALIZE mapC :: (Int -> Int) -> V.Vector (HM.HashMap Int Int)
569+
-> V.Vector (HM.HashMap Int Int) #-}
570+
571+
mapHS :: (Int -> Int) -> HS.HashSet (HM.HashMap Int Int)
572+
-> HS.HashSet (HM.HashMap Int Int)
573+
mapHS f = HS.map (HM.map f)
574+
575+
mapS :: (Int -> Int) -> S.Set (HM.HashMap Int Int) -> S.Set (HM.HashMap Int Int)
576+
mapS f = S.map (HM.map f)
577+
578+
intersectionC :: (Eq k, Hashable k, Foldable f) => f (HM.HashMap k Int)
579+
-> HM.HashMap k Int
580+
intersectionC = foldl' HM.intersection mempty
581+
{-# SPECIALIZE intersectionC :: [HM.HashMap Int Int]
582+
-> HM.HashMap Int Int #-}
583+
{-# SPECIALIZE intersectionC :: V.Vector (HM.HashMap Int Int)
584+
-> HM.HashMap Int Int #-}
585+
{-# SPECIALIZE intersectionC :: HS.HashSet (HM.HashMap Int Int)
586+
-> HM.HashMap Int Int #-}
587+
{-# SPECIALIZE intersectionC :: S.Set (HM.HashMap Int Int)
588+
-> HM.HashMap Int Int #-}
589+
590+
sizeC :: (Eq k, Hashable k, Functor f) => f (HM.HashMap k Int) -> f Int
591+
sizeC = fmap HM.size
592+
{-# SPECIALIZE sizeC :: [HM.HashMap Int Int] -> [Int] #-}
593+
{-# SPECIALIZE sizeC :: V.Vector (HM.HashMap Int Int) -> V.Vector Int #-}
594+
595+
sizeHS :: HS.HashSet (HM.HashMap Int Int) -> HS.HashSet Int
596+
sizeHS = HS.map HM.size
597+
598+
sizeS :: S.Set (HM.HashMap Int Int) -> S.Set Int
599+
sizeS = S.map HM.size
600+
439601
isSubmapOfNaive :: (Eq k, Hashable k) => HM.HashMap k Int -> HM.HashMap k Int -> Bool
440602
isSubmapOfNaive m1 m2 = and [ Just v1 == HM.lookup k1 m2 | (k1,v1) <- HM.toList m1 ]
441603
{-# SPECIALIZE isSubmapOfNaive :: HM.HashMap Int Int -> HM.HashMap Int Int -> Bool #-}

unordered-containers.cabal

+1
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ benchmark benchmarks
215215
bytestring,
216216
containers,
217217
gauge >= 0.2.5 && < 0.3,
218+
vector,
218219
deepseq >= 1.4,
219220
hashable >= 1.0.1.1,
220221
hashmap,

0 commit comments

Comments
 (0)