Skip to content

Commit 9a40921

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 9a40921

File tree

2 files changed

+214
-6
lines changed

2 files changed

+214
-6
lines changed

benchmarks/Benchmarks.hs

+213-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,103 @@ 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+
387+
, bgroup "containerized"
388+
[ bgroup "lookup"
389+
[ bench "List" $ nf (lookupC keysI) listOfHMs
390+
, bench "Vector" $ nf (lookupC keysI) vecOfHMs
391+
, bench "HashSet" $ nf (lookupHS keysI) hsetOfHMs
392+
, bench "Set" $ nf (lookupS keysI) setOfHMs
393+
]
394+
, bgroup "insert"
395+
[ bench "List" $ nf (insertC elemsI) listOfHMs
396+
, bench "Vector" $ nf (insertC elemsI) vecOfHMs
397+
, bench "HashSet" $ nf (insertHS elemsI) hsetOfHMs
398+
, bench "Set" $ nf (insertS elemsI) setOfHMs
399+
]
400+
, bgroup "delete"
401+
[ bench "List" $ nf (deleteC keysI) listOfHMs
402+
, bench "Vector" $ nf (deleteC keysI) vecOfHMs
403+
, bench "HashSet" $ nf (deleteHS keysI) hsetOfHMs
404+
, bench "Set" $ nf (deleteS keysI) setOfHMs
405+
]
406+
, bgroup "union"
407+
[ bench "List" $ whnf unionC listOfHMs
408+
, bench "Vector" $ whnf unionC vecOfHMs
409+
, bench "HashSet" $ whnf unionC hsetOfHMs
410+
, bench "Set" $ whnf unionC setOfHMs
411+
]
412+
, bgroup "map"
413+
[ bench "List" $ nf (mapC (\ v -> v + 1)) listOfHMs
414+
, bench "Vector" $ nf (mapC (\ v -> v + 1)) vecOfHMs
415+
, bench "HashSet" $ nf (mapHS (\ v -> v + 1)) hsetOfHMs
416+
, bench "Set" $ nf (mapS (\ v -> v + 1)) setOfHMs
417+
]
418+
, bgroup "intersection"
419+
[ bench "List" $ whnf intersectionC listOfHMs
420+
, bench "Vector" $ whnf intersectionC vecOfHMs
421+
, bench "HashSet" $ whnf intersectionC hsetOfHMs
422+
, bench "Set" $ whnf intersectionC setOfHMs
423+
]
424+
, bgroup "size"
425+
[ bench "List" $ nf sizeC listOfHMs
426+
, bench "Vector" $ nf sizeC vecOfHMs
427+
, bench "HashSet" $ nf sizeHS hsetOfHMs
428+
, bench "Set" $ nf sizeS setOfHMs
429+
]
430+
]
431+
313432
-- Combine
314433
, bench "union" $ whnf (HM.union hmi) hmi2
315434

316435
-- Transformations
317436
, bench "map" $ whnf (HM.map (\ v -> v + 1)) hmi
318437

319-
-- * Difference and intersection
438+
-- Difference and intersection
320439
, bench "difference" $ whnf (HM.difference hmi) hmi2
321440
, bench "intersection" $ whnf (HM.intersection hmi) hmi2
322441

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

496+
lookupC :: (Eq k, Hashable k, Traversable f) => [k] -> f (HM.HashMap k Int) -> f Int
497+
lookupC = fmap . lookup
498+
{-# SPECIALIZE lookupC :: [Int] -> [HM.HashMap Int Int] -> [Int] #-}
499+
{-# SPECIALIZE lookupC :: [Int] -> V.Vector (HM.HashMap Int Int)
500+
-> V.Vector Int #-}
501+
502+
lookupHS :: [Int] -> HS.HashSet (HM.HashMap Int Int) -> HS.HashSet Int
503+
lookupHS = HS.map . lookup
504+
505+
lookupS :: [Int] -> S.Set (HM.HashMap Int Int) -> S.Set Int
506+
lookupS = S.map . lookup
507+
377508
insert :: (Eq k, Hashable k) => [(k, Int)] -> HM.HashMap k Int
378509
-> HM.HashMap k Int
379510
insert xs m0 = foldl' (\m (k, v) -> HM.insert k v m) m0 xs
@@ -384,6 +515,21 @@ insert xs m0 = foldl' (\m (k, v) -> HM.insert k v m) m0 xs
384515
{-# SPECIALIZE insert :: [(BS.ByteString, Int)] -> HM.HashMap BS.ByteString Int
385516
-> HM.HashMap BS.ByteString Int #-}
386517

518+
insertC :: (Eq k, Hashable k, Traversable f) => [(k, Int)] -> f (HM.HashMap k Int)
519+
-> f (HM.HashMap k Int)
520+
insertC l = fmap (insert l)
521+
{-# SPECIALIZE insertC :: [(Int, Int)] -> [HM.HashMap Int Int]
522+
-> [HM.HashMap Int Int] #-}
523+
{-# SPECIALIZE insertC :: [(Int, Int)] -> V.Vector (HM.HashMap Int Int)
524+
-> V.Vector (HM.HashMap Int Int) #-}
525+
526+
insertHS :: [(Int, Int)] -> HS.HashSet (HM.HashMap Int Int)
527+
-> HS.HashSet (HM.HashMap Int Int)
528+
insertHS l = HS.map (insert l)
529+
530+
insertS :: [(Int, Int)] -> S.Set (HM.HashMap Int Int) -> S.Set (HM.HashMap Int Int)
531+
insertS l = S.map (insert l)
532+
387533
delete :: (Eq k, Hashable k) => [k] -> HM.HashMap k Int -> HM.HashMap k Int
388534
delete xs m0 = foldl' (\m k -> HM.delete k m) m0 xs
389535
{-# SPECIALIZE delete :: [Int] -> HM.HashMap Int Int -> HM.HashMap Int Int #-}
@@ -392,6 +538,21 @@ delete xs m0 = foldl' (\m k -> HM.delete k m) m0 xs
392538
{-# SPECIALIZE delete :: [BS.ByteString] -> HM.HashMap BS.ByteString Int
393539
-> HM.HashMap BS.ByteString Int #-}
394540

541+
deleteC :: (Eq k, Hashable k, Functor f) => [k] -> f (HM.HashMap k Int)
542+
-> f (HM.HashMap k Int)
543+
deleteC = fmap . delete
544+
{-# SPECIALIZE deleteC :: [Int] -> [HM.HashMap Int Int]
545+
-> [HM.HashMap Int Int] #-}
546+
{-# SPECIALIZE deleteC :: [Int] -> V.Vector (HM.HashMap Int Int)
547+
-> V.Vector (HM.HashMap Int Int) #-}
548+
549+
deleteHS :: [Int] -> HS.HashSet (HM.HashMap Int Int)
550+
-> HS.HashSet (HM.HashMap Int Int)
551+
deleteHS = HS.map . delete
552+
553+
deleteS :: [Int] -> S.Set (HM.HashMap Int Int) -> S.Set (HM.HashMap Int Int)
554+
deleteS = S.map . delete
555+
395556
alterInsert :: (Eq k, Hashable k) => [(k, Int)] -> HM.HashMap k Int
396557
-> HM.HashMap k Int
397558
alterInsert xs m0 =
@@ -436,6 +597,52 @@ alterFDelete xs m0 =
436597
{-# SPECIALIZE alterFDelete :: [BS.ByteString] -> HM.HashMap BS.ByteString Int
437598
-> HM.HashMap BS.ByteString Int #-}
438599

600+
unionC :: (Eq k, Hashable k, Foldable f) => f (HM.HashMap k Int)
601+
-> HM.HashMap k Int
602+
unionC = foldl' HM.union mempty
603+
{-# SPECIALIZE unionC :: [HM.HashMap Int Int] -> HM.HashMap Int Int #-}
604+
{-# SPECIALIZE unionC :: V.Vector (HM.HashMap Int Int) -> HM.HashMap Int Int #-}
605+
{-# SPECIALIZE unionC :: HS.HashSet (HM.HashMap Int Int) -> HM.HashMap Int Int #-}
606+
{-# SPECIALIZE unionC :: S.Set (HM.HashMap Int Int) -> HM.HashMap Int Int #-}
607+
608+
mapC :: (Eq k, Hashable k, Functor f) => (Int -> Int) -> f (HM.HashMap k Int)
609+
-> f (HM.HashMap k Int)
610+
mapC f = fmap (HM.map f)
611+
{-# SPECIALIZE mapC :: (Int -> Int) -> [HM.HashMap Int Int]
612+
-> [HM.HashMap Int Int] #-}
613+
{-# SPECIALIZE mapC :: (Int -> Int) -> V.Vector (HM.HashMap Int Int)
614+
-> V.Vector (HM.HashMap Int Int) #-}
615+
616+
mapHS :: (Int -> Int) -> HS.HashSet (HM.HashMap Int Int)
617+
-> HS.HashSet (HM.HashMap Int Int)
618+
mapHS f = HS.map (HM.map f)
619+
620+
mapS :: (Int -> Int) -> S.Set (HM.HashMap Int Int) -> S.Set (HM.HashMap Int Int)
621+
mapS f = S.map (HM.map f)
622+
623+
intersectionC :: (Eq k, Hashable k, Foldable f) => f (HM.HashMap k Int)
624+
-> HM.HashMap k Int
625+
intersectionC = foldl' HM.intersection mempty
626+
{-# SPECIALIZE intersectionC :: [HM.HashMap Int Int]
627+
-> HM.HashMap Int Int #-}
628+
{-# SPECIALIZE intersectionC :: V.Vector (HM.HashMap Int Int)
629+
-> HM.HashMap Int Int #-}
630+
{-# SPECIALIZE intersectionC :: HS.HashSet (HM.HashMap Int Int)
631+
-> HM.HashMap Int Int #-}
632+
{-# SPECIALIZE intersectionC :: S.Set (HM.HashMap Int Int)
633+
-> HM.HashMap Int Int #-}
634+
635+
sizeC :: (Eq k, Hashable k, Functor f) => f (HM.HashMap k Int) -> f Int
636+
sizeC = fmap HM.size
637+
{-# SPECIALIZE sizeC :: [HM.HashMap Int Int] -> [Int] #-}
638+
{-# SPECIALIZE sizeC :: V.Vector (HM.HashMap Int Int) -> V.Vector Int #-}
639+
640+
sizeHS :: HS.HashSet (HM.HashMap Int Int) -> HS.HashSet Int
641+
sizeHS = HS.map HM.size
642+
643+
sizeS :: S.Set (HM.HashMap Int Int) -> S.Set Int
644+
sizeS = S.map HM.size
645+
439646
isSubmapOfNaive :: (Eq k, Hashable k) => HM.HashMap k Int -> HM.HashMap k Int -> Bool
440647
isSubmapOfNaive m1 m2 = and [ Just v1 == HM.lookup k1 m2 | (k1,v1) <- HM.toList m1 ]
441648
{-# 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)