Skip to content

Commit 08bf64a

Browse files
committed
runtime: bound small object sweeping to 100 spans when allocating
Currently, the small object sweeper will sweep until it finds a free slot or there are no more spans of that size class to sweep. In dense heaps, this can cause sweeping for a given size class to take unbounded time, and gets worse with larger heaps. This CL limits the small object sweeper to try at most 100 spans before giving up and allocating a fresh span. Since it's already shown that 100 spans are completely full at that point, the space overhead of this fresh span is at most 1%. This CL is based on an experimental CL by Austin Clements (CL 187817) and is updated to be part of the mcentral implementation, gated by go115NewMCentralImpl. Updates #18155. Change-Id: I37a72c2dcc61dd6f802d1d0eac3683e6642b6ef8 Reviewed-on: https://go-review.googlesource.com/c/go/+/229998 Run-TryBot: Michael Knyszek <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent a136919 commit 08bf64a

File tree

1 file changed

+19
-2
lines changed

1 file changed

+19
-2
lines changed

src/runtime/mcentral.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,31 @@ func (c *mcentral) cacheSpan() *mspan {
106106
if trace.enabled {
107107
traceGCSweepStart()
108108
}
109+
110+
// If we sweep spanBudget spans without finding any free
111+
// space, just allocate a fresh span. This limits the amount
112+
// of time we can spend trying to find free space and
113+
// amortizes the cost of small object sweeping over the
114+
// benefit of having a full free span to allocate from. By
115+
// setting this to 100, we limit the space overhead to 1%.
116+
//
117+
// TODO(austin,mknyszek): This still has bad worst-case
118+
// throughput. For example, this could find just one free slot
119+
// on the 100th swept span. That limits allocation latency, but
120+
// still has very poor throughput. We could instead keep a
121+
// running free-to-used budget and switch to fresh span
122+
// allocation if the budget runs low.
123+
spanBudget := 100
124+
109125
var s *mspan
110126

111127
// Try partial swept spans first.
112128
if s = c.partialSwept(sg).pop(); s != nil {
113129
goto havespan
114130
}
131+
115132
// Now try partial unswept spans.
116-
for {
133+
for ; spanBudget >= 0; spanBudget-- {
117134
s = c.partialUnswept(sg).pop()
118135
if s == nil {
119136
break
@@ -132,7 +149,7 @@ func (c *mcentral) cacheSpan() *mspan {
132149
}
133150
// Now try full unswept spans, sweeping them and putting them into the
134151
// right list if we fail to get a span.
135-
for {
152+
for ; spanBudget >= 0; spanBudget-- {
136153
s = c.fullUnswept(sg).pop()
137154
if s == nil {
138155
break

0 commit comments

Comments
 (0)