Skip to content

Commit 6632631

Browse files
committed
runtime: bound scanobject to ~100 µs
DO NOT SUBMIT. Benchmarks (preferably including mutator latency). Currently the time spent in scanobject is proportional to the size of the object being scanned. Since this time is non-preemptible, large objects can cause significant mutator delays, particularly if a mutator assist picks up a large object. Fix this by splitting large objects into 128 KB "oblets" and scanning at most one oblet at a time. Since we can scan 1–2 MB/ms, this equates to bounding scanobject at roughly 100 µs. This also improves GC parallelism if the heap consists primarily of a small number of very large objects. Fixes #10345. Change-Id: I2a0a179d1d6bf7875dd054b7693dd12d2a340132
1 parent 795809b commit 6632631

File tree

1 file changed

+32
-6
lines changed

1 file changed

+32
-6
lines changed

src/runtime/mgcmark.go

+32-6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ const (
2525
// rootBlockSpans is the number of spans to scan per span
2626
// root.
2727
rootBlockSpans = 8 * 1024 // 64MB worth of spans
28+
29+
// maxObletBytes is the maximum bytes of an object to scan at
30+
// once. Larger objects will be split up into "oblets" of at
31+
// most this size. Since we can scan 1–2 MB/ms, 128 KB bounds
32+
// scan preemption at ~100 µs.
33+
maxObletBytes = 128 << 10
2834
)
2935

3036
// gcMarkRootPrepare queues root scanning jobs (stacks, globals, and
@@ -1108,9 +1114,10 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) {
11081114
}
11091115

11101116
// scanobject scans the object starting at b, adding pointers to gcw.
1111-
// b must point to the beginning of a heap object; scanobject consults
1112-
// the GC bitmap for the pointer mask and the spans for the size of the
1113-
// object.
1117+
// b must point to the beginning of a heap object or an oblet.
1118+
// scanobject consults the GC bitmap for the pointer mask and the
1119+
// spans for the size of the object.
1120+
//
11141121
//go:nowritebarrier
11151122
func scanobject(b uintptr, gcw *gcWork) {
11161123
// Note that arena_used may change concurrently during
@@ -1125,16 +1132,35 @@ func scanobject(b uintptr, gcw *gcWork) {
11251132
arena_start := mheap_.arena_start
11261133
arena_used := mheap_.arena_used
11271134

1128-
// Find bits of the beginning of the object.
1129-
// b must point to the beginning of a heap object, so
1130-
// we can get its bits and span directly.
1135+
// Find the bits for b and the size of the object at b.
1136+
//
1137+
// b is either the beginning of an object, in which case this
1138+
// is the size of the object to scan, or it points to an
1139+
// oblet, in which case we compute the size to scan below.
11311140
hbits := heapBitsForAddr(b)
11321141
s := spanOfUnchecked(b)
11331142
n := s.elemsize
11341143
if n == 0 {
11351144
throw("scanobject n == 0")
11361145
}
11371146

1147+
if n > maxObletBytes {
1148+
// Large object. Break into oblets.
1149+
if b == s.base() {
1150+
// Enqueue the other oblets for later scans.
1151+
for oblet := b + maxObletBytes; oblet < s.base()+s.elemsize; oblet += maxObletBytes {
1152+
if !gcw.putFast(oblet) {
1153+
gcw.put(oblet)
1154+
}
1155+
}
1156+
}
1157+
// Compute the size of the oblet.
1158+
n = s.base() + s.elemsize - b
1159+
if n > maxObletBytes {
1160+
n = maxObletBytes
1161+
}
1162+
}
1163+
11381164
var i uintptr
11391165
for i = 0; i < n; i += sys.PtrSize {
11401166
// Find bits for this word.

0 commit comments

Comments
 (0)