Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC

Commits:

3 changed files:

Changes:

  • rts/PrimOps.cmm
    ... ... @@ -200,6 +200,26 @@ stg_isMutableByteArrayWeaklyPinnedzh ( gcptr mba )
    200 200
      * used to as the LDV profiler will essentially ignore arrays anyways.
    
    201 201
      */
    
    202 202
     
    
    203
    +/* Note [Resizing arrays in-place]
    
    204
    + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    205
    + * We try to shrink or grow bd->free when resizing a MutableByteArray in-place,
    
    206
    + * to reclaim or use slop space at the end of the current block and avoid
    
    207
    + * unnecessary fragmentation/allocation.
    
    208
    + *
    
    209
    + * But we must guarantee that:
    
    210
    + *
    
    211
    + * 1. mba is already at the end of current block (check bd->free).
    
    212
    + *    Otherwise we can't move closures that come after it anyway.
    
    213
    + * 2. It's a nursery block that belongs to the current Capability,
    
    214
    + *    so check rCurrentAlloc (used by allocateMightFail) or
    
    215
    + *    pinned_object_block (used by allocatePinned). There's also no
    
    216
    + *    point if it's an older generation block, the mutator won't
    
    217
    + *    allocate into those blocks anyway.
    
    218
    + *
    
    219
    + * If check fails, fall back to the conservative code path: just zero the slop
    
    220
    + * and return when shrinking, or allocate a new array when growing.
    
    221
    + */
    
    222
    +
    
    203 223
     // shrink size of MutableByteArray in-place
    
    204 224
     stg_shrinkMutableByteArrayzh ( gcptr mba, W_ new_size )
    
    205 225
     // MutableByteArray# s -> Int# -> State# s -> State# s
    
    ... ... @@ -212,20 +232,7 @@ stg_shrinkMutableByteArrayzh ( gcptr mba, W_ new_size )
    212 232
        old_wds = BYTES_TO_WDS(SIZEOF_StgArrBytes) + ROUNDUP_BYTES_TO_WDS(old_size);
    
    213 233
        new_wds = BYTES_TO_WDS(SIZEOF_StgArrBytes) + ROUNDUP_BYTES_TO_WDS(new_size);
    
    214 234
     
    
    215
    -   // Try to shrink bd->free as well, to reclaim slop space at the end
    
    216
    -   // of current block and avoid unnecessary fragmentation. But we
    
    217
    -   // must guarantee that:
    
    218
    -   //
    
    219
    -   // 1. mba is already at the end of current block (check bd->free).
    
    220
    -   //    Otherwise we can't move closures that come after it anyway.
    
    221
    -   // 2. It's a nursery block that belongs to the current Capability,
    
    222
    -   //    so check rCurrentAlloc (used by allocateMightFail) or
    
    223
    -   //    pinned_object_block (used by allocatePinned). There's also no
    
    224
    -   //    point if it's an older generation block, the mutator won't
    
    225
    -   //    allocate into those blocks anyway.
    
    226
    -   //
    
    227
    -   // If check fails, fall back to the conservative code path: just
    
    228
    -   // zero the slop and return.
    
    235
    +   // See Note [Resizing arrays in-place]
    
    229 236
        bd = Bdescr(mba);
    
    230 237
        if (bdescr_free(bd) != mba + WDS(old_wds) ||
    
    231 238
            (bd != StgRegTable_rCurrentAlloc(BaseReg) && bd != Capability_pinned_object_block(MyCapability()))) {
    
    ... ... @@ -258,20 +265,33 @@ stg_shrinkMutableByteArrayzh ( gcptr mba, W_ new_size )
    258 265
     stg_resizzeMutableByteArrayzh ( gcptr mba, W_ new_size )
    
    259 266
     // MutableByteArray# s -> Int# -> State# s -> (# State# s,MutableByteArray# s #)
    
    260 267
     {
    
    268
    +   W_ old_size, old_wds, new_wds, new_free;
    
    269
    +   W_ bd;
    
    270
    +
    
    261 271
        ASSERT(new_size `ge` 0);
    
    262 272
     
    
    263
    -   if (new_size <= StgArrBytes_bytes(mba)) {
    
    273
    +   old_size = StgArrBytes_bytes(mba);
    
    274
    +   if (new_size <= old_size) {
    
    264 275
           call stg_shrinkMutableByteArrayzh(mba, new_size);
    
    265 276
           return (mba);
    
    277
    +   }
    
    278
    +
    
    279
    +   bd = Bdescr(mba);
    
    280
    +   old_wds = BYTES_TO_WDS(SIZEOF_StgArrBytes) + ROUNDUP_BYTES_TO_WDS(old_size);
    
    281
    +   new_wds = BYTES_TO_WDS(SIZEOF_StgArrBytes) + ROUNDUP_BYTES_TO_WDS(new_size);
    
    282
    +   new_free = mba + WDS(new_wds);
    
    283
    +
    
    284
    +   // See Note [Resizing arrays in-place]
    
    285
    +   // we also need to check that we don't grow past the end of current block.
    
    286
    +   if (bdescr_free(bd) == mba + WDS(old_wds) &&
    
    287
    +       (bd == StgRegTable_rCurrentAlloc(BaseReg) || bd == Capability_pinned_object_block(MyCapability())) &&
    
    288
    +       new_free <= bdescr_start(bd) + (TO_W_(bdescr_blocks(bd)) * BLOCK_SIZE)) {
    
    289
    +      bdescr_free(bd) = new_free;
    
    290
    +      StgArrBytes_bytes(mba) = new_size;
    
    291
    +      return (mba);
    
    266 292
        } else {
    
    267 293
           (P_ new_mba) = call stg_newByteArrayzh(new_size);
    
    268 294
     
    
    269
    -      // maybe at some point in the future we may be able to grow the
    
    270
    -      // MBA in-place w/o copying if we know the space after the
    
    271
    -      // current MBA is still available, as often we want to grow the
    
    272
    -      // MBA shortly after we allocated the original MBA. So maybe no
    
    273
    -      // further allocations have occurred by then.
    
    274
    -
    
    275 295
           // copy over old content
    
    276 296
           prim %memcpy(BYTE_ARR_CTS(new_mba), BYTE_ARR_CTS(mba),
    
    277 297
                        StgArrBytes_bytes(mba), SIZEOF_W);
    

  • testsuite/tests/rts/all.T
    ... ... @@ -669,3 +669,5 @@ test('TimeoutQueue',
    669 669
     test('ClosureTable',
    
    670 670
          [req_c, only_ways(['normal', 'debug']), extra_files(['ClosureTable_c.c'])], compile_and_run,
    
    671 671
          ['-debug -O0 ClosureTable_c.c -I{top}/../rts -I{top}/../rts/include'])
    
    672
    +
    
    673
    +test('resizeMutableByteArrayInPlace', [req_cmm, extra_ways(['optasm', 'sanity']), only_ways(['optasm', 'sanity'])], compile_and_run, [''])

  • testsuite/tests/rts/resizeMutableByteArrayInPlace.hs
    1
    +{-# LANGUAGE MagicHash #-}
    
    2
    +{-# LANGUAGE UnboxedTuples #-}
    
    3
    +
    
    4
    +import Control.Monad
    
    5
    +import GHC.Exts
    
    6
    +import GHC.IO
    
    7
    +
    
    8
    +-- Given newByteArray#/newPinnedByteArray#, iterate given number of
    
    9
    +-- rounds: first allocate a MutableByteArray# using the first size,
    
    10
    +-- then resize to the new size, then resize back
    
    11
    +{-# INLINE testResize #-}
    
    12
    +testResize :: (Int# -> State# RealWorld -> (# State# RealWorld, MutableByteArray# RealWorld #)) -> Int -> Int -> Int -> IO ()
    
    13
    +testResize alloc# rounds (I# sz0#) (I# sz1#) =
    
    14
    +  replicateM_ rounds $ IO $ \s0 -> case alloc# sz0# s0 of
    
    15
    +    (# s1, mba0# #) -> case resizeMutableByteArray# mba0# sz1# s1 of
    
    16
    +      (# s2, mba1# #) -> case resizeMutableByteArray# mba1# sz0# s2 of
    
    17
    +        (# s3, _ #) -> (# s3, () #)
    
    18
    +
    
    19
    +main :: IO ()
    
    20
    +main = do
    
    21
    +  testResize newByteArray# 100000 8 64
    
    22
    +  testResize newByteArray# 100000 64 8
    
    23
    +  testResize newPinnedByteArray# 100000 8 64
    
    24
    +  testResize newPinnedByteArray# 100000 64 8