Skip to content

Commit a98d0b6

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 3b3820e commit a98d0b6

File tree

2 files changed

+172
-4
lines changed

2 files changed

+172
-4
lines changed

benchmarks/Benchmarks.hs

Lines changed: 171 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,16 @@ import Test.Tasty.Bench (bench, bgroup, defaultMain, env, nf, whnf)
2020
import qualified Data.ByteString as BS
2121
import qualified "hashmap" Data.HashMap as IHM
2222
import qualified Data.HashMap.Strict as HM
23+
import qualified "unordered-containers" Data.HashSet as HS
2324
import qualified Data.IntMap as IM
25+
import Data.List (foldl')
2426
import qualified Data.Map as M
27+
import Data.Maybe (fromMaybe)
28+
import qualified Data.Set as S
29+
import qualified Data.Vector as V
30+
import GHC.Generics (Generic)
31+
import Prelude hiding (lookup)
32+
2533
import qualified Util.ByteString as UBS
2634
import qualified Util.Int as UI
2735
import qualified Util.String as US
@@ -37,6 +45,8 @@ instance NFData B where
3745
data Env = Env {
3846
n :: !Int,
3947

48+
csz :: !Int, -- container size
49+
4050
elems :: ![(String, Int)],
4151
keys :: ![String],
4252
elemsBS :: ![(BS.ByteString, Int)],
@@ -49,6 +59,11 @@ data Env = Env {
4959
keysBS' :: ![BS.ByteString],
5060
keysI' :: ![Int],
5161

62+
listOfHMs :: ![HM.HashMap Int Int],
63+
vecOfHMs :: !(V.Vector (HM.HashMap Int Int)),
64+
hsetOfHMs :: !(HS.HashSet (HM.HashMap Int Int)),
65+
setOfHMs :: !(S.Set (HM.HashMap Int Int)),
66+
5267
keysDup :: ![String],
5368
keysDupBS :: ![BS.ByteString],
5469
keysDupI :: ![Int],
@@ -79,6 +94,20 @@ setupEnv :: IO Env
7994
setupEnv = do
8095
let n = 2^(12 :: Int)
8196

97+
-- When building a container of hashmaps, 'cn' will be the size of each.
98+
cn = n `div` 16
99+
-- 'csz' is the size of the container of hashmaps.
100+
csz = 2^(7 :: Int)
101+
102+
values = [1..csz*cn]
103+
104+
chop _ [] = []
105+
chop k l =
106+
let (taken, left) = splitAt k l
107+
in taken : chop k left
108+
109+
vals = chop cn values
110+
82111
elems = zip keys [1..n]
83112
keys = US.rnd 8 n
84113
elemsBS = zip keysBS [1..n]
@@ -91,6 +120,11 @@ setupEnv = do
91120
keysBS' = UBS.rnd' 8 n
92121
keysI' = UI.rnd' (n+n) n
93122

123+
listOfHMs = zipWith (\x y -> HM.fromList (zip x y)) (repeat keysI) vals
124+
vecOfHMs = V.fromList listOfHMs
125+
hsetOfHMs = HS.fromList listOfHMs
126+
setOfHMs = S.fromList listOfHMs
127+
94128
keysDup = US.rnd 2 n
95129
keysDupBS = UBS.rnd 2 n
96130
keysDupI = UI.rnd (n`div`4) n
@@ -128,8 +162,8 @@ main = do
128162
[
129163
#ifdef BENCH_containers_Map
130164
env setupEnv $ \ ~(Env{..}) ->
131-
-- * Comparison to other data structures
132-
-- ** Map
165+
-- Comparison to other data structures
166+
-- Map
133167
bgroup "Map"
134168
[ bgroup "lookup"
135169
[ bench "String" $ whnf (lookupM keys) m
@@ -231,7 +265,7 @@ main = do
231265

232266
env setupEnv $ \ ~(Env{..}) ->
233267
bgroup "HashMap"
234-
[ -- * Basic interface
268+
[ -- Basic interface
235269
bgroup "lookup"
236270
[ bench "String" $ whnf (lookup keys) hm
237271
, bench "ByteString" $ whnf (lookup keysBS) hmbs
@@ -313,6 +347,51 @@ main = do
313347
, bench "Int" $ whnf (isSubmapOfNaive hmiSubset) hmi
314348
]
315349

350+
, bgroup "containerized"
351+
[ bgroup "lookup"
352+
[ bench "List" $ nf (lookupC keysI) listOfHMs
353+
, bench "Vector" $ nf (lookupC keysI) vecOfHMs
354+
, bench "HashSet" $ nf (lookupHS keysI) hsetOfHMs
355+
, bench "Set" $ nf (lookupS keysI) setOfHMs
356+
]
357+
, bgroup "insert"
358+
[ bench "List" $ nf (insertC elemsI) listOfHMs
359+
, bench "Vector" $ nf (insertC elemsI) vecOfHMs
360+
, bench "HashSet" $ nf (insertHS elemsI) hsetOfHMs
361+
, bench "Set" $ nf (insertS elemsI) setOfHMs
362+
]
363+
, bgroup "delete"
364+
[ bench "List" $ nf (deleteC keysI) listOfHMs
365+
, bench "Vector" $ nf (deleteC keysI) vecOfHMs
366+
, bench "HashSet" $ nf (deleteHS keysI) hsetOfHMs
367+
, bench "Set" $ nf (deleteS keysI) setOfHMs
368+
]
369+
, bgroup "union"
370+
[ bench "List" $ whnf unionC listOfHMs
371+
, bench "Vector" $ whnf unionC vecOfHMs
372+
, bench "HashSet" $ whnf unionC hsetOfHMs
373+
, bench "Set" $ whnf unionC setOfHMs
374+
]
375+
, bgroup "map"
376+
[ bench "List" $ nf (mapC (\ v -> v + 1)) listOfHMs
377+
, bench "Vector" $ nf (mapC (\ v -> v + 1)) vecOfHMs
378+
, bench "HashSet" $ nf (mapHS (\ v -> v + 1)) hsetOfHMs
379+
, bench "Set" $ nf (mapS (\ v -> v + 1)) setOfHMs
380+
]
381+
, bgroup "intersection"
382+
[ bench "List" $ whnf intersectionC listOfHMs
383+
, bench "Vector" $ whnf intersectionC vecOfHMs
384+
, bench "HashSet" $ whnf intersectionC hsetOfHMs
385+
, bench "Set" $ whnf intersectionC setOfHMs
386+
]
387+
, bgroup "size"
388+
[ bench "List" $ nf sizeC listOfHMs
389+
, bench "Vector" $ nf sizeC vecOfHMs
390+
, bench "HashSet" $ nf sizeHS hsetOfHMs
391+
, bench "Set" $ nf sizeS setOfHMs
392+
]
393+
]
394+
316395
-- Combine
317396
, bgroup "union"
318397
[ bench "Int" $ whnf (HM.union hmi) hmi2
@@ -327,7 +406,7 @@ main = do
327406
-- Transformations
328407
, bench "map" $ whnf (HM.map (\ v -> v + 1)) hmi
329408

330-
-- * Difference and intersection
409+
-- Difference and intersection
331410
, bench "difference" $ whnf (HM.difference hmi) hmi2
332411

333412
-- Folds
@@ -389,6 +468,18 @@ lookup xs m = foldl' (\z k -> fromMaybe z (HM.lookup k m)) 0 xs
389468
{-# SPECIALIZE lookup :: [BS.ByteString] -> HM.HashMap BS.ByteString Int
390469
-> Int #-}
391470

471+
lookupC :: (Eq k, Hashable k, Traversable f) => [k] -> f (HM.HashMap k Int) -> f Int
472+
lookupC = fmap . lookup
473+
{-# SPECIALIZE lookupC :: [Int] -> [HM.HashMap Int Int] -> [Int] #-}
474+
{-# SPECIALIZE lookupC :: [Int] -> V.Vector (HM.HashMap Int Int)
475+
-> V.Vector Int #-}
476+
477+
lookupHS :: [Int] -> HS.HashSet (HM.HashMap Int Int) -> HS.HashSet Int
478+
lookupHS = HS.map . lookup
479+
480+
lookupS :: [Int] -> S.Set (HM.HashMap Int Int) -> S.Set Int
481+
lookupS = S.map . lookup
482+
392483
insert :: (Eq k, Hashable k) => [(k, Int)] -> HM.HashMap k Int
393484
-> HM.HashMap k Int
394485
insert xs m0 = foldl' (\m (k, v) -> HM.insert k v m) m0 xs
@@ -399,6 +490,21 @@ insert xs m0 = foldl' (\m (k, v) -> HM.insert k v m) m0 xs
399490
{-# SPECIALIZE insert :: [(BS.ByteString, Int)] -> HM.HashMap BS.ByteString Int
400491
-> HM.HashMap BS.ByteString Int #-}
401492

493+
insertC :: (Eq k, Hashable k, Traversable f) => [(k, Int)] -> f (HM.HashMap k Int)
494+
-> f (HM.HashMap k Int)
495+
insertC l = fmap (insert l)
496+
{-# SPECIALIZE insertC :: [(Int, Int)] -> [HM.HashMap Int Int]
497+
-> [HM.HashMap Int Int] #-}
498+
{-# SPECIALIZE insertC :: [(Int, Int)] -> V.Vector (HM.HashMap Int Int)
499+
-> V.Vector (HM.HashMap Int Int) #-}
500+
501+
insertHS :: [(Int, Int)] -> HS.HashSet (HM.HashMap Int Int)
502+
-> HS.HashSet (HM.HashMap Int Int)
503+
insertHS l = HS.map (insert l)
504+
505+
insertS :: [(Int, Int)] -> S.Set (HM.HashMap Int Int) -> S.Set (HM.HashMap Int Int)
506+
insertS l = S.map (insert l)
507+
402508
delete :: (Eq k, Hashable k) => [k] -> HM.HashMap k Int -> HM.HashMap k Int
403509
delete xs m0 = foldl' (\m k -> HM.delete k m) m0 xs
404510
{-# SPECIALIZE delete :: [Int] -> HM.HashMap Int Int -> HM.HashMap Int Int #-}
@@ -407,6 +513,21 @@ delete xs m0 = foldl' (\m k -> HM.delete k m) m0 xs
407513
{-# SPECIALIZE delete :: [BS.ByteString] -> HM.HashMap BS.ByteString Int
408514
-> HM.HashMap BS.ByteString Int #-}
409515

516+
deleteC :: (Eq k, Hashable k, Functor f) => [k] -> f (HM.HashMap k Int)
517+
-> f (HM.HashMap k Int)
518+
deleteC = fmap . delete
519+
{-# SPECIALIZE deleteC :: [Int] -> [HM.HashMap Int Int]
520+
-> [HM.HashMap Int Int] #-}
521+
{-# SPECIALIZE deleteC :: [Int] -> V.Vector (HM.HashMap Int Int)
522+
-> V.Vector (HM.HashMap Int Int) #-}
523+
524+
deleteHS :: [Int] -> HS.HashSet (HM.HashMap Int Int)
525+
-> HS.HashSet (HM.HashMap Int Int)
526+
deleteHS = HS.map . delete
527+
528+
deleteS :: [Int] -> S.Set (HM.HashMap Int Int) -> S.Set (HM.HashMap Int Int)
529+
deleteS = S.map . delete
530+
410531
alterInsert :: (Eq k, Hashable k) => [(k, Int)] -> HM.HashMap k Int
411532
-> HM.HashMap k Int
412533
alterInsert xs m0 =
@@ -451,6 +572,52 @@ alterFDelete xs m0 =
451572
{-# SPECIALIZE alterFDelete :: [BS.ByteString] -> HM.HashMap BS.ByteString Int
452573
-> HM.HashMap BS.ByteString Int #-}
453574

575+
unionC :: (Eq k, Hashable k, Foldable f) => f (HM.HashMap k Int)
576+
-> HM.HashMap k Int
577+
unionC = foldl' HM.union mempty
578+
{-# SPECIALIZE unionC :: [HM.HashMap Int Int] -> HM.HashMap Int Int #-}
579+
{-# SPECIALIZE unionC :: V.Vector (HM.HashMap Int Int) -> HM.HashMap Int Int #-}
580+
{-# SPECIALIZE unionC :: HS.HashSet (HM.HashMap Int Int) -> HM.HashMap Int Int #-}
581+
{-# SPECIALIZE unionC :: S.Set (HM.HashMap Int Int) -> HM.HashMap Int Int #-}
582+
583+
mapC :: (Eq k, Hashable k, Functor f) => (Int -> Int) -> f (HM.HashMap k Int)
584+
-> f (HM.HashMap k Int)
585+
mapC f = fmap (HM.map f)
586+
{-# SPECIALIZE mapC :: (Int -> Int) -> [HM.HashMap Int Int]
587+
-> [HM.HashMap Int Int] #-}
588+
{-# SPECIALIZE mapC :: (Int -> Int) -> V.Vector (HM.HashMap Int Int)
589+
-> V.Vector (HM.HashMap Int Int) #-}
590+
591+
mapHS :: (Int -> Int) -> HS.HashSet (HM.HashMap Int Int)
592+
-> HS.HashSet (HM.HashMap Int Int)
593+
mapHS f = HS.map (HM.map f)
594+
595+
mapS :: (Int -> Int) -> S.Set (HM.HashMap Int Int) -> S.Set (HM.HashMap Int Int)
596+
mapS f = S.map (HM.map f)
597+
598+
intersectionC :: (Eq k, Hashable k, Foldable f) => f (HM.HashMap k Int)
599+
-> HM.HashMap k Int
600+
intersectionC = foldl' HM.intersection mempty
601+
{-# SPECIALIZE intersectionC :: [HM.HashMap Int Int]
602+
-> HM.HashMap Int Int #-}
603+
{-# SPECIALIZE intersectionC :: V.Vector (HM.HashMap Int Int)
604+
-> HM.HashMap Int Int #-}
605+
{-# SPECIALIZE intersectionC :: HS.HashSet (HM.HashMap Int Int)
606+
-> HM.HashMap Int Int #-}
607+
{-# SPECIALIZE intersectionC :: S.Set (HM.HashMap Int Int)
608+
-> HM.HashMap Int Int #-}
609+
610+
sizeC :: (Eq k, Hashable k, Functor f) => f (HM.HashMap k Int) -> f Int
611+
sizeC = fmap HM.size
612+
{-# SPECIALIZE sizeC :: [HM.HashMap Int Int] -> [Int] #-}
613+
{-# SPECIALIZE sizeC :: V.Vector (HM.HashMap Int Int) -> V.Vector Int #-}
614+
615+
sizeHS :: HS.HashSet (HM.HashMap Int Int) -> HS.HashSet Int
616+
sizeHS = HS.map HM.size
617+
618+
sizeS :: S.Set (HM.HashMap Int Int) -> S.Set Int
619+
sizeS = S.map HM.size
620+
454621
isSubmapOfNaive :: (Eq k, Hashable k) => HM.HashMap k Int -> HM.HashMap k Int -> Bool
455622
isSubmapOfNaive m1 m2 = and [ Just v1 == HM.lookup k1 m2 | (k1,v1) <- HM.toList m1 ]
456623
{-# SPECIALIZE isSubmapOfNaive :: HM.HashMap Int Int -> HM.HashMap Int Int -> Bool #-}

unordered-containers.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ benchmark benchmarks
131131
containers,
132132
deepseq,
133133
hashable,
134+
vector,
134135
hashmap,
135136
mtl,
136137
random,

0 commit comments

Comments
 (0)