[Git][ghc/ghc][wip/gc/nonmoving-pinned] nonmoving: Teach allocatePinned() to allocate into nonmoving heap
Ben Gamari
gitlab at gitlab.haskell.org
Sun Jul 26 21:02:31 UTC 2020
Ben Gamari pushed to branch wip/gc/nonmoving-pinned at Glasgow Haskell Compiler / GHC
Commits:
5efe1dc9 by Ben Gamari at 2020-07-26T17:02:21-04:00
nonmoving: Teach allocatePinned() to allocate into nonmoving heap
The allocatePinned() function is used to allocate pinned memory (e.g.
for newPinnedByteArray#)
- - - - -
5 changed files:
- rts/sm/NonMoving.c
- rts/sm/NonMoving.h
- rts/sm/NonMovingMark.c
- rts/sm/NonMovingScav.c
- rts/sm/Storage.c
Changes:
=====================================
rts/sm/NonMoving.c
=====================================
@@ -474,6 +474,24 @@ Mutex concurrent_coll_finished_lock;
* remembered set during the preparatory GC. This allows us to safely skip the
* non-moving write barrier without jeopardizing the snapshot invariant.
*
+ *
+ * Note [Allocating pinned objects into the non-moving heap]
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Under the moving collector small, pinned ByteArray#s are allocated by
+ * Storage.c:allocatePinned() into a per-capability accumulator block which is
+ * filled in a bump-pointer fashion. While this scheme is simple, it can lead
+ * to very poor fragmentation behavior as objects become unreachable: a single
+ * live ByteArray# can keep an entire block of memory alive.
+ *
+ * When the non-moving collector is in use we can do better by allocating small
+ * pinned objects directly into the non-moving heap.
+ *
+ * One wrinkle here is that pinned ByteArrays may have alignment requirements
+ * which requires that we insert padding zero-words before the beginning of the
+ * object. We must be certain to account for this padding when inspecting the
+ * object.
+ *
*/
memcount nonmoving_live_words = 0;
=====================================
rts/sm/NonMoving.h
=====================================
@@ -73,11 +73,17 @@ struct NonmovingAllocator {
// allocators cover block sizes of 2^NONMOVING_ALLOCA0 to
// 2^(NONMOVING_ALLOCA0 + NONMOVING_ALLOCA_CNT) (in bytes)
+// The largest allocator class must be at least LARGE_OBJECT_THRESHOLD in size
+// as Storage.c:allocatePinned will allocate small pinned allocations into the
+// non-moving heap.
#define NONMOVING_ALLOCA_CNT 12
// maximum number of free segments to hold on to
#define NONMOVING_MAX_FREE 16
+// block size of largest allocator in bytes.
+#define NONMOVING_MAX_BLOCK_SZ (1 << (NONMOVING_ALLOCA0 + NONMOVING_ALLOCA_CNT))
+
struct NonmovingHeap {
struct NonmovingAllocator *allocators[NONMOVING_ALLOCA_CNT];
// free segment list. This is a cache where we keep up to
=====================================
rts/sm/NonMovingMark.c
=====================================
@@ -1359,6 +1359,11 @@ mark_closure (MarkQueue *queue, const StgClosure *p0, StgClosure **origin)
// Trace pointers
/////////////////////////////////////////////////////
+ // Find beginning of object.
+ // See Note [Allocating pinned objects into the non-moving heap].
+ while (*(StgPtr*) q == NULL)
+ q = (StgClosure *) ((StgPtr*) q + 1);
+
const StgInfoTable *info = get_itbl(p);
switch (info->type) {
=====================================
rts/sm/NonMovingScav.c
=====================================
@@ -11,9 +11,18 @@
#include "MarkWeak.h" // scavengeLiveWeak
void
-nonmovingScavengeOne (StgClosure *q)
+nonmovingScavengeOne (StgClosure *q0)
{
+ StgClosure *q = q0;
+
+ // N.B. There may be a gap before the first word of the closure in the case
+ // of an aligned ByteArray# as allocated by allocatePinned().
+ // See Note [Allocating pinned objects into the non-moving heap].
+ while (*(StgPtr*) q == NULL)
+ q = (StgClosure *) ((StgPtr*) q + 1);
+
ASSERT(LOOKS_LIKE_CLOSURE_PTR(q));
+
StgPtr p = (StgPtr)q;
const StgInfoTable *info = get_itbl(q);
const bool saved_eager_promotion = gct->eager_promotion;
=====================================
rts/sm/Storage.c
=====================================
@@ -1165,6 +1165,23 @@ allocatePinned (Capability *cap, W_ n /*words*/, W_ alignment /*bytes*/, W_ alig
const StgWord alignment_w = alignment / sizeof(W_);
+ // If the non-moving collector is enabled then we can allocate small,
+ // pinned allocations directly into the non-moving heap. This is a bit more
+ // expensive up-front but reduces fragmentation and is worthwhile since
+ // pinned allocations are often long-lived..
+ //
+ // See Note [Allocating pinned objects into the non-moving heap].
+ if (RTS_UNLIKELY(RtsFlags.GcFlags.useNonmoving)
+ && n * sizeof(W_) + alignment_w <= NONMOVING_MAX_BLOCK_SZ)
+ {
+ p = nonmovingAllocate(cap, n + alignment_w);
+ W_ off_w = ALIGN_WITH_OFF_W(p, alignment, align_off);
+ MEMSET_IF_PROFILING_W(p, 0, off_w);
+ p += off_w;
+ MEMSET_IF_PROFILING_W(p + n, 0, alignment_w - off_w - 1);
+ return p;
+ }
+
// If the request is for a large object, then allocate()
// will give us a pinned object anyway.
if (n >= LARGE_OBJECT_THRESHOLD/sizeof(W_)) {
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5efe1dc9f0774fa78f6fe7fa4bb19bd59e7b187c
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/5efe1dc9f0774fa78f6fe7fa4bb19bd59e7b187c
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20200726/a2f4bc70/attachment-0001.html>
More information about the ghc-commits
mailing list