| ... |
... |
@@ -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);
|