Skip to content

Commit a62b572

Browse files
committed
runtime: scavenge huge spans first
This change adds two new treap iteration types: one for large unscavenged spans (contain at least one huge page) and one for small unscavenged spans. This allows us to scavenge the huge spans first by first iterating over the large ones, then the small ones. Also, since we now depend on physHugePageSize being a power of two, ensure that that's the case when it's retrieved from the OS. For #30333. Change-Id: I51662740205ad5e4905404a0856f5f2b2d2a5680 Reviewed-on: https://go-review.googlesource.com/c/go/+/174399 Run-TryBot: Michael Knyszek <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent fa8470a commit a62b572

File tree

5 files changed

+57
-25
lines changed

5 files changed

+57
-25
lines changed

src/runtime/export_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ type TreapIterType treapIterType
550550

551551
const (
552552
TreapIterScav TreapIterType = TreapIterType(treapIterScav)
553+
TreapIterHuge = TreapIterType(treapIterHuge)
553554
TreapIterBits = treapIterBits
554555
)
555556

src/runtime/mgclarge.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,8 @@ type treapIterType uint8
273273

274274
const (
275275
treapIterScav treapIterType = 1 << iota // scavenged spans
276-
treapIterBits = iota
276+
treapIterHuge // spans containing at least one huge page
277+
treapIterBits = iota
277278
)
278279

279280
// treapIterFilter is a bitwise filter of different spans by binary
@@ -318,6 +319,9 @@ func (s *mspan) treapFilter() treapIterFilter {
318319
if s.scavenged {
319320
have |= treapIterScav
320321
}
322+
if s.hugePages() > 0 {
323+
have |= treapIterHuge
324+
}
321325
return treapIterFilter(uint32(1) << (0x1f & have))
322326
}
323327

src/runtime/mheap.go

+41-19
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,25 @@ func (h *mheap) coalesce(s *mspan) {
521521
}
522522
}
523523

524+
// hugePages returns the number of aligned physical huge pages in the memory
525+
// regioned owned by this mspan.
526+
func (s *mspan) hugePages() uintptr {
527+
if physHugePageSize == 0 || s.npages < physHugePageSize/pageSize {
528+
return 0
529+
}
530+
start := s.base()
531+
end := start + s.npages*pageSize
532+
if physHugePageSize > pageSize {
533+
// Round start and end in.
534+
start = (start + physHugePageSize - 1) &^ (physHugePageSize - 1)
535+
end &^= physHugePageSize - 1
536+
}
537+
if start < end {
538+
return (end - start) / physHugePageSize
539+
}
540+
return 0
541+
}
542+
524543
func (s *mspan) scavenge() uintptr {
525544
// start and end must be rounded in, otherwise madvise
526545
// will round them *out* and release more memory
@@ -1324,27 +1343,30 @@ func (h *mheap) scavengeLocked(nbytes uintptr) {
13241343
h.scavengeCredit -= nbytes
13251344
return
13261345
}
1327-
// Iterate over the unscavenged spans in the treap backwards (from highest
1328-
// address to lowest address) scavenging spans until we've reached our
1329-
// quota of nbytes.
13301346
released := uintptr(0)
1331-
for t := h.free.end(treapIterScav, 0); released < nbytes && t.valid(); {
1332-
s := t.span()
1333-
start, end := s.physPageBounds()
1334-
if start >= end {
1335-
// This span doesn't cover at least one physical page, so skip it.
1336-
t = t.prev()
1337-
continue
1347+
// Iterate over spans with huge pages first, then spans without.
1348+
const mask = treapIterScav | treapIterHuge
1349+
for _, match := range []treapIterType{treapIterHuge, 0} {
1350+
// Iterate over the treap backwards (from highest address to lowest address)
1351+
// scavenging spans until we've reached our quota of nbytes.
1352+
for t := h.free.end(mask, match); released < nbytes && t.valid(); {
1353+
s := t.span()
1354+
start, end := s.physPageBounds()
1355+
if start >= end {
1356+
// This span doesn't cover at least one physical page, so skip it.
1357+
t = t.prev()
1358+
continue
1359+
}
1360+
n := t.prev()
1361+
h.free.erase(t)
1362+
released += s.scavenge()
1363+
// Now that s is scavenged, we must eagerly coalesce it
1364+
// with its neighbors to prevent having two spans with
1365+
// the same scavenged state adjacent to each other.
1366+
h.coalesce(s)
1367+
t = n
1368+
h.free.insert(s)
13381369
}
1339-
n := t.prev()
1340-
h.free.erase(t)
1341-
released += s.scavenge()
1342-
// Now that s is scavenged, we must eagerly coalesce it
1343-
// with its neighbors to prevent having two spans with
1344-
// the same scavenged state adjacent to each other.
1345-
h.coalesce(s)
1346-
t = n
1347-
h.free.insert(s)
13481370
}
13491371
// If we over-scavenged, turn that extra amount into credit.
13501372
if released > nbytes {

src/runtime/os_linux.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -270,16 +270,19 @@ func getHugePageSize() uintptr {
270270
return 0
271271
}
272272
n := read(fd, noescape(unsafe.Pointer(&numbuf[0])), int32(len(numbuf)))
273+
closefd(fd)
273274
if n <= 0 {
274-
closefd(fd)
275275
return 0
276276
}
277277
l := n - 1 // remove trailing newline
278278
v, ok := atoi(slicebytetostringtmp(numbuf[:l]))
279279
if !ok || v < 0 {
280280
v = 0
281281
}
282-
closefd(fd)
282+
if v&(v-1) != 0 {
283+
// v is not a power of 2
284+
return 0
285+
}
283286
return uintptr(v)
284287
}
285288

src/runtime/treap_test.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ func TestTreapFilter(t *testing.T) {
4141
mask, match runtime.TreapIterType
4242
filter runtime.TreapIterFilter // expected filter
4343
}{
44-
{0, 0, 0x3},
45-
{runtime.TreapIterScav, 0, 0x1},
46-
{runtime.TreapIterScav, runtime.TreapIterScav, 0x2},
44+
{0, 0, 0xf},
45+
{runtime.TreapIterScav, 0, 0x5},
46+
{runtime.TreapIterScav, runtime.TreapIterScav, 0xa},
47+
{runtime.TreapIterScav | runtime.TreapIterHuge, runtime.TreapIterHuge, 0x4},
48+
{runtime.TreapIterScav | runtime.TreapIterHuge, 0, 0x1},
4749
{0, runtime.TreapIterScav, 0x0},
4850
}
4951
for _, it := range iterTypes {

0 commit comments

Comments
 (0)