Skip to content

Commit bb1b662

Browse files
committed
Use randomly structured Set and Map in benchmarks
Trees constructed from [1..n] have a specific structure of perfect binary trees linked together. We shuffle the list so that the tree is generated from repeated insertions instead, which forms a random structure that should be more representative of average use cases. Most benchmarks show an increase in measured times, the amount varying from 10% to 80%.
1 parent 3117213 commit bb1b662

File tree

6 files changed

+84
-28
lines changed

6 files changed

+84
-28
lines changed

containers-tests/benchmarks/Map.hs

+15-12
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ import Data.Coerce
1717
import Prelude hiding (lookup)
1818

1919
import Utils.Fold (foldBenchmarks, foldWithKeyBenchmarks)
20+
import Utils.Random (shuffle)
2021

2122
main = do
22-
let m = M.fromAscList elems :: M.Map Int Int
23-
m_even = M.fromAscList elems_even :: M.Map Int Int
24-
m_odd = M.fromAscList elems_odd :: M.Map Int Int
23+
let m = M.fromList elems :: M.Map Int Int
24+
m_even = M.fromList elems_even :: M.Map Int Int
25+
m_odd = M.fromList elems_odd :: M.Map Int Int
2526
evaluate $ rnf [m, m_even, m_odd]
26-
evaluate $ rnf [elems_rev, elems_asc, elems_desc]
27+
evaluate $ rnf
28+
[elems_distinct_asc, elems_distinct_desc, elems_asc, elems_desc]
2729
defaultMain
2830
[ bench "lookup absent" $ whnf (lookup evens) m_odd
2931
, bench "lookup present" $ whnf (lookup evens) m_even
@@ -80,7 +82,7 @@ main = do
8082
, bench "updateLookupWithKey delete" $ whnf (upd' (const Nothing) evens) m
8183
, bench "mapMaybe" $ whnf (M.mapMaybe maybeDel) m
8284
, bench "mapMaybeWithKey" $ whnf (M.mapMaybeWithKey (const maybeDel)) m
83-
, bench "lookupIndex" $ whnf (lookupIndex keys) m
85+
, bench "lookupIndex" $ whnf (lookupIndex keys_distinct_asc) m
8486
, bench "union" $ whnf (M.union m_even) m_odd
8587
, bench "difference" $ whnf (M.difference m) m_even
8688
, bench "intersection" $ whnf (M.intersection m) m_even
@@ -93,9 +95,9 @@ main = do
9395
, bench "fromDescList" $ whnf M.fromDescList elems_desc
9496
, bench "fromDescListWithKey" $
9597
whnf (M.fromDescListWithKey sumkv) elems_desc
96-
, bench "fromDistinctAscList" $ whnf M.fromDistinctAscList elems
98+
, bench "fromDistinctAscList" $ whnf M.fromDistinctAscList elems_distinct_asc
9799
, bench "fromDistinctAscList:fusion" $ whnf (\n -> M.fromDistinctAscList [(i,i) | i <- [1..n]]) bound
98-
, bench "fromDistinctDescList" $ whnf M.fromDistinctDescList elems_rev
100+
, bench "fromDistinctDescList" $ whnf M.fromDistinctDescList elems_distinct_desc
99101
, bench "fromDistinctDescList:fusion" $ whnf (\n -> M.fromDistinctDescList [(i,i) | i <- [n,n-1..1]]) bound
100102
, bench "minView" $ whnf (\m' -> case M.minViewWithKey m' of {Nothing -> 0; Just ((k,v),m'') -> k+v+M.size m''}) (M.fromAscList $ zip [1..10::Int] [100..110::Int])
101103
, bench "eq" $ whnf (\m' -> m' == m') m -- worst case, compares everything
@@ -106,17 +108,18 @@ main = do
106108
]
107109
where
108110
bound = 2^12
109-
elems = zip keys values
111+
elems = shuffle elems_distinct_asc
110112
elems_even = zip evens evens
111113
elems_odd = zip odds odds
112-
elems_rev = reverse elems
114+
elems_distinct_asc = zip keys_distinct_asc values
115+
elems_distinct_desc = reverse elems_distinct_asc
113116
keys_asc = map (`div` 2) [1..bound] -- [0,1,1,2,2..]
114117
elems_asc = zip keys_asc values
115118
keys_desc = map (`div` 2) [bound,bound-1..1] -- [..2,2,1,1,0]
116119
elems_desc = zip keys_desc values
117-
keys = [1..bound]
118-
evens = [2,4..bound]
119-
odds = [1,3..bound]
120+
keys_distinct_asc = [1..bound]
121+
evens = shuffle [2,4..bound]
122+
odds = shuffle [1,3..bound]
120123
values = [1..bound]
121124
sumkv k v1 v2 = k + v1 + v2
122125
consPair k v xs = (k, v) : xs

containers-tests/benchmarks/Set.hs

+13-10
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ import Data.List (foldl')
99
import qualified Data.Set as S
1010

1111
import Utils.Fold (foldBenchmarks)
12+
import Utils.Random (shuffle)
1213

1314
main = do
14-
let s = S.fromAscList elems :: S.Set Int
15-
s_even = S.fromAscList elems_even :: S.Set Int
16-
s_odd = S.fromAscList elems_odd :: S.Set Int
15+
let s = S.fromList elems :: S.Set Int
16+
s_even = S.fromList elems_even :: S.Set Int
17+
s_odd = S.fromList elems_odd :: S.Set Int
1718
strings_s = S.fromList strings
1819
evaluate $ rnf [s, s_even, s_odd]
19-
evaluate $ rnf [elems_rev, elems_asc, elems_desc]
20+
evaluate $ rnf
21+
[elems_distinct_asc, elems_distinct_desc, elems_asc, elems_desc]
2022
defaultMain
2123
[ bench "member" $ whnf (member elems) s
2224
, bench "insert" $ whnf (ins elems) S.empty
@@ -35,10 +37,10 @@ main = do
3537
, bench "fromList" $ whnf S.fromList elems
3638
, bench "fromList-desc" $ whnf S.fromList elems_desc
3739
, bench "fromAscList" $ whnf S.fromAscList elems_asc
38-
, bench "fromDistinctAscList" $ whnf S.fromDistinctAscList elems
40+
, bench "fromDistinctAscList" $ whnf S.fromDistinctAscList elems_distinct_asc
3941
, bench "fromDistinctAscList:fusion" $ whnf (\n -> S.fromDistinctAscList [1..n]) bound
4042
, bench "fromDescList" $ whnf S.fromDescList elems_desc
41-
, bench "fromDistinctDescList" $ whnf S.fromDistinctDescList elems_rev
43+
, bench "fromDistinctDescList" $ whnf S.fromDistinctDescList elems_distinct_desc
4244
, bench "fromDistinctDescList:fusion" $ whnf (\n -> S.fromDistinctDescList [n,n-1..1]) bound
4345
, bench "disjoint:false" $ whnf (S.disjoint s) s_even
4446
, bench "disjoint:true" $ whnf (S.disjoint s_odd) s_even
@@ -61,10 +63,11 @@ main = do
6163
]
6264
where
6365
bound = 2^12
64-
elems = [1..bound]
65-
elems_even = [2,4..bound]
66-
elems_odd = [1,3..bound]
67-
elems_rev = reverse elems
66+
elems_distinct_asc = [1..bound]
67+
elems_distinct_desc = reverse elems_distinct_asc
68+
elems = shuffle elems_distinct_asc
69+
elems_even = shuffle [2,4..bound]
70+
elems_odd = shuffle [1,3..bound]
6871
elems_asc = map (`div` 2) [1..bound] -- [0,1,1,2,2..]
6972
elems_desc = map (`div` 2) [bound,bound-1..1] -- [..2,2,1,1,0]
7073
strings = map show elems

containers-tests/benchmarks/SetOperations/SetOperations-Map.hs

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ module Main where
33
import Data.Map as C
44
import SetOperations
55

6+
import Utils.Random (shuffle)
7+
68
main :: IO ()
7-
main = benchmark (\xs -> fromList [(x, x) | x <- xs]) True
9+
main = benchmark (\xs -> fromList [(x, x) | x <- shuffle xs]) True
810
[ ("union", C.union)
911
, ("difference", C.difference)
1012
, ("intersection", C.intersection)

containers-tests/benchmarks/SetOperations/SetOperations-Set.hs

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ module Main where
33
import Data.Set as C
44
import SetOperations
55

6+
import Utils.Random (shuffle)
7+
68
main :: IO ()
7-
main = benchmark fromList True
9+
main = benchmark (fromList . shuffle) True
810
[ ("union", C.union)
911
, ("difference", C.difference)
1012
, ("intersection", C.intersection)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module Utils.Random
2+
( shuffle
3+
) where
4+
5+
import Data.List (unfoldr)
6+
import qualified Data.Sequence as Seq
7+
import System.Random (StdGen, mkStdGen, randomR)
8+
9+
-- O(n log n). Deterministic shuffle. Implements Fisher-Yates.
10+
shuffle :: [a] -> [a]
11+
shuffle xs0 = unfoldr f (gen, Seq.fromList xs0)
12+
where
13+
f (g, xs)
14+
| Seq.null xs = Nothing
15+
| otherwise = Just (x, (g', xs'))
16+
where
17+
(i, g') = randomR (0, Seq.length xs - 1) g
18+
x = Seq.index xs i
19+
xs' = Seq.deleteAt i xs
20+
21+
gen :: StdGen
22+
gen = mkStdGen 42

containers-tests/containers-tests.cabal

+28-4
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,11 @@ benchmark map-benchmarks
167167
hs-source-dirs: benchmarks
168168
main-is: Map.hs
169169
ghc-options: -O2
170+
build-depends:
171+
random >=1.0 && <1.3
172+
173+
other-modules:
174+
Utils.Random
170175

171176
other-modules:
172177
Utils.Fold
@@ -196,7 +201,7 @@ benchmark sequence-benchmarks
196201
main-is: Sequence.hs
197202
ghc-options: -O2
198203
build-depends:
199-
random >=0 && <1.2
204+
random >=1.0 && <1.3
200205
, transformers
201206

202207
other-modules:
@@ -212,6 +217,11 @@ benchmark set-benchmarks
212217
hs-source-dirs: benchmarks
213218
main-is: Set.hs
214219
ghc-options: -O2
220+
build-depends:
221+
random >=1.0 && <1.3
222+
223+
other-modules:
224+
Utils.Random
215225

216226
other-modules:
217227
Utils.Fold
@@ -227,7 +237,7 @@ benchmark graph-benchmarks
227237
main-is: Graph.hs
228238
ghc-options: -O2
229239
build-depends:
230-
random >=0 && <1.2
240+
random >=1.0 && <1.3
231241

232242
benchmark set-operations-intmap
233243
import: benchmark-deps, warnings
@@ -251,19 +261,33 @@ benchmark set-operations-map
251261
import: benchmark-deps, warnings
252262
default-language: Haskell2010
253263
type: exitcode-stdio-1.0
254-
hs-source-dirs: benchmarks/SetOperations
264+
hs-source-dirs:
265+
benchmarks/SetOperations
266+
benchmarks
255267
main-is: SetOperations-Map.hs
256268
other-modules: SetOperations
257269
ghc-options: -O2
270+
build-depends:
271+
random >=1.0 && <1.3
272+
273+
other-modules:
274+
Utils.Random
258275

259276
benchmark set-operations-set
260277
import: benchmark-deps, warnings
261278
default-language: Haskell2010
262279
type: exitcode-stdio-1.0
263-
hs-source-dirs: benchmarks/SetOperations
280+
hs-source-dirs:
281+
benchmarks/SetOperations
282+
benchmarks
264283
main-is: SetOperations-Set.hs
265284
other-modules: SetOperations
266285
ghc-options: -O2
286+
build-depends:
287+
random >=1.0 && <1.3
288+
289+
other-modules:
290+
Utils.Random
267291

268292
benchmark lookupge-intmap
269293
import: benchmark-deps, warnings

0 commit comments

Comments
 (0)