diff options
author | Michael Anthony Knyszek <mknyszek@google.com> | 2019-08-21 00:24:25 +0000 |
---|---|---|
committer | Michael Knyszek <mknyszek@google.com> | 2019-11-07 19:14:27 +0000 |
commit | 73317080e12234defb59f84e2b5b15f69650b5d5 (patch) | |
tree | 299810e966e35857625c1323fe0da5d3c6cd4c88 /src/runtime/export_test.go | |
parent | 39e8cb0faac7785f89b21246a45e8cf8d5bc7d95 (diff) | |
download | go-73317080e12234defb59f84e2b5b15f69650b5d5.tar.gz go-73317080e12234defb59f84e2b5b15f69650b5d5.zip |
runtime: add scavenging code for new page allocator
This change adds a scavenger for the new page allocator along with
tests. The scavenger walks over the heap backwards once per GC, looking
for memory to scavenge. It walks across the heap without any lock held,
searching optimistically. If it finds what appears to be a scavenging
candidate it acquires the heap lock and attempts to verify it. Upon
verification it then scavenges.
Notably, unlike the old scavenger, it doesn't show any preference for
huge pages and instead follows a more strict last-page-first policy.
Updates #35112.
Change-Id: I0621ef73c999a471843eab2d1307ae5679dd18d6
Reviewed-on: https://go-review.googlesource.com/c/go/+/195697
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Diffstat (limited to 'src/runtime/export_test.go')
-rw-r--r-- | src/runtime/export_test.go | 71 |
1 files changed, 64 insertions, 7 deletions
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 39cedee3a1..cca78e5ec3 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -38,7 +38,7 @@ var Nanotime = nanotime var NetpollBreak = netpollBreak var Usleep = usleep -var PageSize = pageSize +var PhysPageSize = physPageSize var PhysHugePageSize = physHugePageSize var NetpollGenericInit = netpollGenericInit @@ -733,6 +733,7 @@ func RunGetgThreadSwitchTest() { } const ( + PageSize = pageSize PallocChunkPages = pallocChunkPages ) @@ -825,6 +826,26 @@ func StringifyPallocBits(b *PallocBits, r BitRange) string { return str } +// Expose pallocData for testing. +type PallocData pallocData + +func (d *PallocData) FindScavengeCandidate(searchIdx uint, min, max uintptr) (uint, uint) { + return (*pallocData)(d).findScavengeCandidate(searchIdx, min, max) +} +func (d *PallocData) AllocRange(i, n uint) { (*pallocData)(d).allocRange(i, n) } +func (d *PallocData) ScavengedSetRange(i, n uint) { + (*pallocData)(d).scavenged.setRange(i, n) +} +func (d *PallocData) PallocBits() *PallocBits { + return (*PallocBits)(&(*pallocData)(d).pallocBits) +} +func (d *PallocData) Scavenged() *PallocBits { + return (*PallocBits)(&(*pallocData)(d).scavenged) +} + +// Expose fillAligned for testing. +func FillAligned(x uint64, m uint) uint64 { return fillAligned(x, m) } + // Expose chunk index type. type ChunkIdx chunkIdx @@ -837,8 +858,14 @@ func (p *PageAlloc) Free(base, npages uintptr) { (*pageAlloc)(p).free(base, n func (p *PageAlloc) Bounds() (ChunkIdx, ChunkIdx) { return ChunkIdx((*pageAlloc)(p).start), ChunkIdx((*pageAlloc)(p).end) } -func (p *PageAlloc) PallocBits(i ChunkIdx) *PallocBits { - return (*PallocBits)(&((*pageAlloc)(p).chunks[i])) +func (p *PageAlloc) PallocData(i ChunkIdx) *PallocData { + return (*PallocData)(&((*pageAlloc)(p).chunks[i])) +} +func (p *PageAlloc) Scavenge(nbytes uintptr) (r uintptr) { + systemstack(func() { + r = (*pageAlloc)(p).scavenge(nbytes) + }) + return } // BitRange represents a range over a bitmap. @@ -847,14 +874,25 @@ type BitRange struct { } // NewPageAlloc creates a new page allocator for testing and -// initializes it with the chunks map. Each key represents a chunk -// index and each value is a series of bit ranges to set within that -// chunk. -func NewPageAlloc(chunks map[ChunkIdx][]BitRange) *PageAlloc { +// initializes it with the scav and chunks maps. Each key in these maps +// represents a chunk index and each value is a series of bit ranges to +// set within each bitmap's chunk. +// +// The initialization of the pageAlloc preserves the invariant that if a +// scavenged bit is set the alloc bit is necessarily unset, so some +// of the bits described by scav may be cleared in the final bitmap if +// ranges in chunks overlap with them. +// +// scav is optional, and if nil, the scavenged bitmap will be cleared +// (as opposed to all 1s, which it usually is). Furthermore, every +// chunk index in scav must appear in chunks; ones that do not are +// ignored. +func NewPageAlloc(chunks, scav map[ChunkIdx][]BitRange) *PageAlloc { p := new(pageAlloc) // We've got an entry, so initialize the pageAlloc. p.init(new(mutex), nil) + p.test = true for i, init := range chunks { addr := chunkBase(chunkIdx(i)) @@ -864,6 +902,25 @@ func NewPageAlloc(chunks map[ChunkIdx][]BitRange) *PageAlloc { // Initialize the bitmap and update pageAlloc metadata. chunk := &p.chunks[chunkIndex(addr)] + + // Clear all the scavenged bits which grow set. + chunk.scavenged.clearRange(0, pallocChunkPages) + + // Apply scavenge state if applicable. + if scav != nil { + if scvg, ok := scav[i]; ok { + for _, s := range scvg { + // Ignore the case of s.N == 0. setRange doesn't handle + // it and it's a no-op anyway. + if s.N != 0 { + chunk.scavenged.setRange(s.I, s.N) + } + } + } + } + p.resetScavengeAddr() + + // Apply alloc state. for _, s := range init { // Ignore the case of s.N == 0. allocRange doesn't handle // it and it's a no-op anyway. |