-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathMutable.hs
More file actions
191 lines (162 loc) · 5.95 KB
/
Copy pathMutable.hs
File metadata and controls
191 lines (162 loc) · 5.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneKindSignatures #-}
-- Uncomment the line below to observe the generated (optimised) Core. It will
-- land in a file named “Array.dump-simpl”
-- {-# OPTIONS_GHC -ddump-simpl -ddump-to-file -dsuppress-all -dsuppress-uniques #-}
module Bench.Data.Array.Mutable (benchmarks) where
import Control.DeepSeq (rnf)
import qualified Data.Array.Mutable.Linear as Array.Linear
import qualified Data.Array.Mutable.Linear as Array.Linear.Array
import qualified Data.Foldable
import Data.Functor.Compose
import Data.Kind
import qualified Data.Sequence
import qualified Data.Unrestricted.Linear as Linear
import qualified Data.Vector
import Prelude.Linear (($), (&))
import qualified Prelude.Linear as Linear
import Test.Tasty.Bench
import Prelude hiding (($))
arr_size :: Int
arr_size = 1_000
benchmarks :: Benchmark
benchmarks =
bgroup
"arrays"
$ runImpls
[ bAlloc,
bToList,
bMap,
bReads,
bSets
]
--------------------------------------------------------------------------------
data Impl where
Impl :: String -> (forall arr. (ArrayThing arr) => arr Int %1 -> ()) -> Impl
runImpls :: [Impl] -> [Benchmark]
runImpls = map (runImpl arr_size)
runImpl :: Int -> Impl -> Benchmark
runImpl sz0 (Impl name impl) =
bgroup
name
[ bench "Data.Array.Mutable.Linear" $ whnf (runLinear impl) sz0,
bench "Data.Vector" $ whnf (runDataVector (cleanup impl)) sz0,
bench "Data.Sequence" $ whnf (runSequence (cleanup impl)) sz0
]
where
runLinear :: (Array.Linear.Array Int %1 -> ()) -> Int -> ()
runLinear cb sz = Linear.unur (Array.Linear.alloc sz 0 (\a -> Linear.move (cb a)))
runDataVector :: (Data.Vector.Vector Int -> ()) -> Int -> ()
runDataVector cb sz = cb (Data.Vector.replicate sz 0)
runSequence :: (Data.Sequence.Seq Int -> ()) -> Int -> ()
runSequence cb sz = cb (Data.Sequence.replicate sz 0)
{-# INLINE runImpl #-}
type ArrayThing :: (Type -> Type) -> Constraint
class ArrayThing arr where
size :: arr a %1 -> (Linear.Ur Int, arr a)
get :: Int -> arr a %1 -> (Linear.Ur a, arr a)
set :: Int -> a -> arr a %1 -> arr a
toList :: arr a %1 -> Linear.Ur [a]
amap :: (a -> b) -> arr a %1 -> arr b
-- | Note: I [Arnaud Spiwack] initially thought I could use
-- 'Consumable'/'consume' for this. But it doesn't work because the natural
-- 'consume' function for `Ur x` doesn't evaluate the `x` at all. We need to
-- evaluate the `x` in the 'Vector' instance.
force :: arr a %1 -> ()
type UArrayThing :: (Type -> Type) -> Constraint
class UArrayThing arr where
usize :: arr a -> Int
uget :: Int -> arr a -> a
uset :: Int -> a -> arr a -> arr a
utoList :: arr a -> [a]
uamap :: (a -> b) -> arr a -> arr b
uforce :: arr a -> ()
instance ArrayThing Array.Linear.Array where
size = Array.Linear.Array.size
get = Array.Linear.Array.unsafeGet
set = Array.Linear.Array.unsafeSet
toList = Array.Linear.Array.toList
amap = Array.Linear.Array.map
force = Linear.consume
instance (UArrayThing arr) => ArrayThing (Compose Linear.Ur arr) where
size (Compose (Linear.Ur arr)) = (Linear.Ur (usize arr), Compose (Linear.Ur arr))
get i (Compose (Linear.Ur arr)) = (Linear.Ur (uget i arr), Compose (Linear.Ur arr))
set i a (Compose (Linear.Ur arr)) = Compose (Linear.Ur (uset i a arr))
toList (Compose (Linear.Ur arr)) = Linear.Ur (utoList arr)
amap f (Compose (Linear.Ur arr)) = Compose (Linear.Ur (uamap f arr))
force (Compose (Linear.Ur arr)) = uforce arr
instance UArrayThing Data.Vector.Vector where
usize = Data.Vector.length
uget i v = v Data.Vector.! i
uset i a v = v Data.Vector.// [(i, a)]
utoList = Data.Vector.toList
uamap = Data.Vector.map
uforce = (`seq` ())
instance UArrayThing Data.Sequence.Seq where
usize = Data.Sequence.length
uget i s = Data.Sequence.index s i
uset = Data.Sequence.update
utoList = Data.Foldable.toList
uamap = fmap
-- I'm not sure about this one: on the one hand it forces the data structure
-- to be allocated. On the other hand, it will do an extra traversal. Maybe
-- there's a better comparison that can be done.
uforce s = (foldMap (\_ -> Strict) s) `seq` ()
cleanup :: ((Compose Linear.Ur f a) %1 -> b) -> (f a -> b)
cleanup k a = k (Compose (Linear.Ur a))
data Strict = Strict
instance Semigroup Strict where
Strict <> x = x
instance Monoid Strict where
mempty = Strict
--------------------------------------------------------------------------------
bToList :: Impl
bToList = Impl "toList" impl
where
impl :: (ArrayThing arr) => arr Int %1 -> ()
impl arr = arr & toList & Linear.lift rnf & Linear.unur
bMap :: Impl
bMap = Impl "map" impl
where
impl :: (ArrayThing arr) => arr Int %1 -> ()
impl arr =
case arr & amap (+ 1) & get 5 of
(Linear.Ur _, arr') -> force arr'
bReads :: Impl
bReads = Impl "reads" impl
where
impl :: (ArrayThing arr) => arr Int %1 -> ()
impl arr0 =
case size arr0 of
(Linear.Ur sz, arr) -> go 0 sz arr
where
go :: (ArrayThing arr) => Int -> Int -> arr Int %1 -> ()
go start end arr
| start < end =
case get start arr of
(Linear.Ur i, arr') -> i `Linear.seq` go (start + 1) end arr'
| otherwise = force arr
bAlloc :: Impl
bAlloc = Impl "alloc" impl
where
impl :: (ArrayThing arr) => arr Int %1 -> ()
impl = force
bSets :: Impl
bSets = Impl "successive writes (very unfair to vector)" impl
where
impl :: (ArrayThing arr) => arr Int %1 -> ()
impl arr0 =
case size arr0 of
(Linear.Ur sz, arr) -> go 0 sz arr
where
go :: (ArrayThing arr) => Int -> Int -> arr Int %1 -> ()
go start end arr
| start < end =
go (start + 1) end Linear.$ set start 42 arr
| otherwise = force arr