Skip to content

Commit b6f1591

Browse files
author
Jaro Reinders
committed
Use pointer reversal and explain aseq
1 parent d52f952 commit b6f1591

File tree

1 file changed

+58
-22
lines changed

1 file changed

+58
-22
lines changed

src/Fleet/Array.hs

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,24 @@ the latest version of the array. Otherwise the performance regresses to O(k),
2121
where k is the number of changes between the version you are accessing and the
2222
latest version.
2323
-}
24-
module Fleet.Array (Array, fromList, toList, (!), index, set, copy, swap, aseq) where
24+
module Fleet.Array
25+
( Array
26+
, fromList
27+
, toList
28+
, (!)
29+
, index
30+
, set
31+
, copy
32+
, swap
33+
, pseq
34+
) where
2535

2636
import Data.Tuple (Solo (MkSolo))
2737
import GHC.Exts hiding (fromList, toList, Lifted)
2838

2939
import Data.Kind (Type)
3040
import GHC.IO.Unsafe (unsafeDupablePerformIO)
41+
import GHC.Conc (pseq)
3142

3243
import Fleet.Array.MutVar
3344
import Fleet.Array.Lift
@@ -54,10 +65,6 @@ pattern Diff op v = Lift (Diff# op v)
5465
instance Show a => Show (Array a) where
5566
show xs = "fromList " ++ show (toList xs)
5667

57-
-- | Sequencing array operations.
58-
aseq :: a -> b -> b
59-
aseq x y = x `seq` lazy y
60-
6168
-- | Convert a list into an array. O(n)
6269
fromList :: [a] -> Array a
6370
fromList xs = unsafeDupablePerformIO $ do
@@ -92,6 +99,34 @@ toList (A v) = unsafeDupablePerformIO $ do
9299
go 0
93100

94101
-- | Indexing an array. O(1)
102+
--
103+
-- __WARNING:__ If you were to write your own 'swap' function. You might be
104+
-- tempted to write it like this:
105+
--
106+
-- > swap :: Int -> Int -> Array a -> Array a
107+
-- > swap !i !j !xs = set i (xs ! j) (set j (xs ! i) xs)
108+
--
109+
-- Unfortunately, this leaves the order between the reads and writes undefined.
110+
-- And in practice, GHC picks the wrong order. To enforce that reads happen
111+
-- before writes, you can use 'pseq' like this:
112+
--
113+
-- > swap !i !j !xs =
114+
-- > let
115+
-- > x = xs ! i
116+
-- > y = xs ! j
117+
-- > in x `pseq` y `pseq` set i y (set j x xs)
118+
--
119+
-- If you want to avoid forcing the elements in the array, then you can use
120+
-- 'index' like this:
121+
--
122+
-- > swap !i !j !xs =
123+
-- > let
124+
-- > x = index i xs
125+
-- > y = index j xs
126+
-- > in x `pseq` y `pseq` set i (getSolo y) (set j (getSolo x) xs)
127+
--
128+
-- In the future, we hope to write a GHC plugin that can automatically detect
129+
-- when pseq is necessary in common cases.
95130
{-# INLINE (!) #-}
96131
(!) :: Array a -> Int -> a
97132
A v0 ! i0 = unsafeDupablePerformIO (go v0 i0) where
@@ -141,27 +176,28 @@ appOp arr (Swap i j) = do
141176
writeMutArray arr i y
142177
writeMutArray arr j x
143178

144-
{-# INLINE appDiffOp #-}
145-
appDiffOp :: Op a -> Array a -> Array a
146-
appDiffOp op (A v) = unsafeDupablePerformIO $ do
179+
{-# INLINE reversePointers #-}
180+
reversePointers :: ArrayVar a -> IO (MutArray a)
181+
reversePointers v = do
147182
dat <- readMutVar v
148183
case dat of
149-
xs@(Current arr) -> do
184+
Current arr -> pure arr
185+
Diff op v' -> do
186+
arr <- reversePointers v'
150187
op' <- invert arr op
151188
appOp arr op
152-
v' <- newMutVar xs
153-
writeMutVar v (Diff op' v')
154-
pure (A v')
155-
Diff op' v' -> do
156-
-- TODO: pointer inversion instead of copy
157-
-- first invert all pointers until Current
158-
-- then apply all updates until back at v
159-
-- then do the same as above
160-
arr <- copyInternal v'
161-
appOp arr op'
162-
appOp arr op
163-
v'' <- newMutVar (Current arr)
164-
pure (A v'')
189+
writeMutVar v' (Diff op' v)
190+
pure arr
191+
192+
{-# INLINE appDiffOp #-}
193+
appDiffOp :: Op a -> Array a -> Array a
194+
appDiffOp op (A v) = unsafeDupablePerformIO $ do
195+
arr <- reversePointers v
196+
op' <- invert arr op
197+
appOp arr op
198+
v' <- newMutVar (Current arr)
199+
writeMutVar v (Diff op' v')
200+
pure (A v')
165201

166202
-- | Update the array element at a given position to a new value. O(1)
167203
{-# INLINE set #-}

0 commit comments

Comments
 (0)