Skip to content

Commit 5636f09

Browse files
treeowlsjakobi
authored andcommitted
Avoid boxing arrays
Previously, we used a lot of things that looked like runST (act >>= unsafeFreeze) The trouble is that GHC can't unbox past the `runRW#` primitive that `runST` is based on. So this actually allocates an `Array` constructor which we will then throw away immediately. The way to avoid this is to call `runRW#` manually to produce a raw `SmallArray#`, then apply the `Array` constructor on the outside.
1 parent 7237826 commit 5636f09

File tree

2 files changed

+48
-24
lines changed

2 files changed

+48
-24
lines changed

Data/HashMap/Internal.hs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ module Data.HashMap.Internal
120120
, two
121121
, unionArrayBy
122122
, update32
123-
, update32M
123+
, update32Mut
124124
, update32With'
125125
, updateOrConcatWith
126126
, updateOrConcatWithKey
@@ -740,7 +740,10 @@ collision h !e1 !e2 =
740740

741741
-- | Create a 'BitmapIndexed' or 'Full' node.
742742
bitmapIndexedOrFull :: Bitmap -> A.Array (HashMap k v) -> HashMap k v
743-
bitmapIndexedOrFull b ary
743+
-- I don't know if it ever matters in context (once inlined),
744+
-- but the Core for this function looks a lot nicer if we force
745+
-- the array argument manually. I don't know why that is.
746+
bitmapIndexedOrFull b !ary
744747
| b == fullNodeMask = Full ary
745748
| otherwise = BitmapIndexed b ary
746749
{-# INLINE bitmapIndexedOrFull #-}
@@ -1615,7 +1618,7 @@ unionWithKey f = go 0
16151618
-- | Strict in the result of @f@.
16161619
unionArrayBy :: (a -> a -> a) -> Bitmap -> Bitmap -> A.Array a -> A.Array a
16171620
-> A.Array a
1618-
unionArrayBy f b1 b2 ary1 ary2 = A.run $ do
1621+
unionArrayBy f !b1 !b2 !ary1 !ary2 = A.run $ do
16191622
let b' = b1 .|. b2
16201623
mary <- A.new_ (popCount b')
16211624
-- iterate over nonzero bits of b1 .|. b2
@@ -2218,16 +2221,16 @@ subsetArray cmpV ary1 ary2 = A.length ary1 <= A.length ary2 && A.all inAry2 ary1
22182221

22192222
-- | /O(n)/ Update the element at the given position in this array.
22202223
update32 :: A.Array e -> Int -> e -> A.Array e
2221-
update32 ary idx b = runST (update32M ary idx b)
2224+
update32 ary idx b = A.run (update32Mut ary idx b)
22222225
{-# INLINE update32 #-}
22232226

22242227
-- | /O(n)/ Update the element at the given position in this array.
2225-
update32M :: A.Array e -> Int -> e -> ST s (A.Array e)
2226-
update32M ary idx b = do
2228+
update32Mut :: A.Array e -> Int -> e -> ST s (A.MArray s e)
2229+
update32Mut ary idx b = do
22272230
mary <- clone ary
22282231
A.write mary idx b
2229-
A.unsafeFreeze mary
2230-
{-# INLINE update32M #-}
2232+
return mary
2233+
{-# INLINE update32Mut #-}
22312234

22322235
-- | /O(n)/ Update the element at the given position in this array, by applying a function to it.
22332236
update32With' :: A.Array e -> Int -> (e -> e) -> A.Array e

Data/HashMap/Internal/Array.hs

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ import GHC.Exts (Int (..), SmallArray#, SmallMutableArray#,
8686
cloneSmallMutableArray#, copySmallArray#,
8787
copySmallMutableArray#, indexSmallArray#,
8888
newSmallArray#, readSmallArray#,
89-
reallyUnsafePtrEquality#, sizeofSmallArray#,
89+
reallyUnsafePtrEquality#, runRW#, sizeofSmallArray#,
9090
sizeofSmallMutableArray#, tagToEnum#,
9191
thawSmallArray#, unsafeCoerce#,
9292
unsafeFreezeSmallArray#, unsafeThawSmallArray#,
@@ -205,13 +205,17 @@ new_ :: Int -> ST s (MArray s a)
205205
new_ n = new n undefinedElem
206206

207207
singleton :: a -> Array a
208-
singleton x = runST (singletonM x)
208+
singleton x = run (singletonMut x)
209209
{-# INLINE singleton #-}
210210

211211
singletonM :: a -> ST s (Array a)
212212
singletonM x = new 1 x >>= unsafeFreeze
213213
{-# INLINE singletonM #-}
214214

215+
singletonMut :: a -> ST s (MArray s a)
216+
singletonMut x = new 1 x
217+
{-# INLINE singletonMut #-}
218+
215219
pair :: a -> a -> Array a
216220
pair x y = run $ do
217221
ary <- new 2 x
@@ -263,7 +267,17 @@ unsafeThaw ary
263267
{-# INLINE unsafeThaw #-}
264268

265269
run :: (forall s . ST s (MArray s e)) -> Array e
266-
run act = runST $ act >>= unsafeFreeze
270+
#if MIN_VERSION_base(4,9,0)
271+
-- GHC can't unbox across the runRW# boundary, so we apply the Array constructor
272+
-- on the outside.
273+
run (ST act) =
274+
case runRW# $ \s ->
275+
case act s of { (# s', MArray mary #) ->
276+
unsafeFreezeSmallArray# mary s' } of
277+
(# _, ary #) -> Array ary
278+
#else
279+
run act = runST (act >>= unsafeFreeze)
280+
#endif
267281
{-# INLINE run #-}
268282

269283
-- | Unsafely copy the elements of an array. Array bounds are not checked.
@@ -300,33 +314,37 @@ trim mary n = cloneM mary 0 n >>= unsafeFreeze
300314
-- | /O(n)/ Insert an element at the given position in this array,
301315
-- increasing its size by one.
302316
insert :: Array e -> Int -> e -> Array e
303-
insert ary idx b = runST (insertM ary idx b)
317+
insert ary idx b = run (insertMut ary idx b)
304318
{-# INLINE insert #-}
305319

306320
-- | /O(n)/ Insert an element at the given position in this array,
307321
-- increasing its size by one.
308322
insertM :: Array e -> Int -> e -> ST s (Array e)
309-
insertM ary idx b =
323+
insertM ary idx b = insertMut ary idx b >>= unsafeFreeze
324+
{-# INLINE insertM #-}
325+
326+
insertMut :: Array e -> Int -> e -> ST s (MArray s e)
327+
insertMut ary idx b =
310328
CHECK_BOUNDS("insertM", count + 1, idx)
311329
do mary <- new (count+1) b
312330
copy ary 0 mary 0 idx
313331
copy ary idx mary (idx+1) (count-idx)
314-
unsafeFreeze mary
332+
return mary
315333
where !count = length ary
316-
{-# INLINE insertM #-}
334+
{-# INLINE insertMut #-}
317335

318336
-- | /O(n)/ Update the element at the given position in this array.
319337
update :: Array e -> Int -> e -> Array e
320-
update ary idx b = runST (updateM ary idx b)
338+
update ary idx b = run (updateM ary idx b)
321339
{-# INLINE update #-}
322340

323341
-- | /O(n)/ Update the element at the given position in this array.
324-
updateM :: Array e -> Int -> e -> ST s (Array e)
342+
updateM :: Array e -> Int -> e -> ST s (MArray s e)
325343
updateM ary idx b =
326344
CHECK_BOUNDS("updateM", count, idx)
327345
do mary <- thaw ary 0 count
328346
write mary idx b
329-
unsafeFreeze mary
347+
return mary
330348
where !count = length ary
331349
{-# INLINE updateM #-}
332350

@@ -421,18 +439,18 @@ thaw !ary !_o@(I# o#) _n@(I# n#) =
421439
-- | /O(n)/ Delete an element at the given position in this array,
422440
-- decreasing its size by one.
423441
delete :: Array e -> Int -> Array e
424-
delete ary idx = runST (deleteM ary idx)
442+
delete ary idx = run (deleteM ary idx)
425443
{-# INLINE delete #-}
426444

427445
-- | /O(n)/ Delete an element at the given position in this array,
428446
-- decreasing its size by one.
429-
deleteM :: Array e -> Int -> ST s (Array e)
447+
deleteM :: Array e -> Int -> ST s (MArray s e)
430448
deleteM ary idx = do
431449
CHECK_BOUNDS("deleteM", count, idx)
432450
do mary <- new_ (count-1)
433451
copy ary 0 mary 0 idx
434452
copy ary (idx+1) mary idx (count-(idx+1))
435-
unsafeFreeze mary
453+
return mary
436454
where !count = length ary
437455
{-# INLINE deleteM #-}
438456

@@ -442,9 +460,10 @@ map f = \ ary ->
442460
in run $ do
443461
mary <- new_ n
444462
go ary mary 0 n
463+
return mary
445464
where
446465
go ary mary i n
447-
| i >= n = return mary
466+
| i >= n = return ()
448467
| otherwise = do
449468
x <- indexM ary i
450469
write mary i $ f x
@@ -458,9 +477,10 @@ map' f = \ ary ->
458477
in run $ do
459478
mary <- new_ n
460479
go ary mary 0 n
480+
return mary
461481
where
462482
go ary mary i n
463-
| i >= n = return mary
483+
| i >= n = return ()
464484
| otherwise = do
465485
x <- indexM ary i
466486
write mary i $! f x
@@ -473,8 +493,9 @@ fromList n xs0 =
473493
run $ do
474494
mary <- new_ n
475495
go xs0 mary 0
496+
return mary
476497
where
477-
go [] !mary !_ = return mary
498+
go [] !_ !_ = return ()
478499
go (x:xs) mary i = do write mary i x
479500
go xs mary (i+1)
480501

0 commit comments

Comments
 (0)