Skip to content

Commit 5b2d4a4

Browse files
committed
runtime: avoid zeroing scavenged memory
On Linux, memory returned to the kernel via MADV_DONTNEED is guaranteed to be zero-filled on its next use. This commit leverages this kernel behavior to avoid a redundant software zeroing pass in the runtime, improving performance. Signed-off-by: Lance Yang <[email protected]>
1 parent 12ec09f commit 5b2d4a4

File tree

9 files changed

+70
-0
lines changed

9 files changed

+70
-0
lines changed

src/runtime/mem.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ func sysUnused(v unsafe.Pointer, n uintptr) {
7070
sysUnusedOS(v, n)
7171
}
7272

73+
// sysNeedZeroAfterUnused reports whether memory returned by sysUnused must be
74+
// zeroed for use.
75+
func sysNeedZeroAfterUnused() bool {
76+
return sysNeedZeroAfterUnusedOS()
77+
}
78+
7379
// sysUsed transitions a memory region from Prepared to Ready. It notifies the
7480
// operating system that the memory region is needed and ensures that the region
7581
// may be safely accessed. This is typically a no-op on systems that don't have

src/runtime/mem_aix.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,7 @@ func sysMapOS(v unsafe.Pointer, n uintptr, _ string) {
7979
throw("runtime: cannot map pages in arena address space")
8080
}
8181
}
82+
83+
func sysNeedZeroAfterUnusedOS() bool {
84+
return true
85+
}

src/runtime/mem_bsd.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,7 @@ func sysMapOS(v unsafe.Pointer, n uintptr, _ string) {
8585
throw("runtime: cannot map pages in arena address space")
8686
}
8787
}
88+
89+
func sysNeedZeroAfterUnusedOS() bool {
90+
return true
91+
}

src/runtime/mem_darwin.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,7 @@ func sysMapOS(v unsafe.Pointer, n uintptr, _ string) {
7474
throw("runtime: cannot map pages in arena address space")
7575
}
7676
}
77+
78+
func sysNeedZeroAfterUnusedOS() bool {
79+
return true
80+
}

src/runtime/mem_linux.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,7 @@ func sysMapOS(v unsafe.Pointer, n uintptr, vmaName string) {
188188
sysNoHugePageOS(v, n)
189189
}
190190
}
191+
192+
func sysNeedZeroAfterUnusedOS() bool {
193+
return debug.madvdontneed == 0
194+
}

src/runtime/mem_sbrk.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,3 +296,7 @@ func sysReserveAlignedSbrk(size, align uintptr) (unsafe.Pointer, uintptr) {
296296
})
297297
return unsafe.Pointer(p), size
298298
}
299+
300+
func sysNeedZeroAfterUnusedOS() bool {
301+
return true
302+
}

src/runtime/mem_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,7 @@ func sysReserveOS(v unsafe.Pointer, n uintptr, _ string) unsafe.Pointer {
132132

133133
func sysMapOS(v unsafe.Pointer, n uintptr, _ string) {
134134
}
135+
136+
func sysNeedZeroAfterUnusedOS() bool {
137+
return true
138+
}

src/runtime/mheap.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,13 @@ func (h *mheap) setSpans(base, npage uintptr, s *mspan) {
10741074
//
10751075
// There are no locking constraints on this method.
10761076
func (h *mheap) allocNeedsZero(base, npage uintptr) (needZero bool) {
1077+
// If these pages were scavenged (returned to the OS), the kernel guarantees
1078+
// they will be zero-filled on next use (fault-in), so we can treat them as
1079+
// already zeroed and skip explicit clearing.
1080+
if !sysNeedZeroAfterUnused() && h.pages.isScavenged(base, npage) {
1081+
return false
1082+
}
1083+
10771084
for npage > 0 {
10781085
ai := arenaIndex(base)
10791086
ha := h.arenas[ai.l1()][ai.l2()]

src/runtime/mpagealloc.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,39 @@ func (p *pageAlloc) enableChunkHugePages() {
481481
}
482482
}
483483

484+
// isScavenged returns true if the page range [base, base+npages*pageSize) is
485+
// fully scavenged.
486+
//
487+
// p.mheapLock must be held.
488+
func (p *pageAlloc) isScavenged(base, npages uintptr) bool {
489+
limit := base + npages*pageSize - 1
490+
sc, ec := chunkIndex(base), chunkIndex(limit)
491+
si, ei := chunkPageIndex(base), chunkPageIndex(limit)
492+
493+
if sc == ec {
494+
// The range doesn't cross any chunk boundaries.
495+
chunk := p.chunkOf(sc)
496+
return chunk.scavenged.isRangeSet(si, ei+1-si)
497+
}
498+
// The range crosses at least one chunk boundary.
499+
chunk := p.chunkOf(sc)
500+
if !chunk.scavenged.isRangeSet(si, pallocChunkPages-si) {
501+
return false
502+
}
503+
for c := sc + 1; c < ec; c++ {
504+
chunk := p.chunkOf(c)
505+
if !chunk.scavenged.isRangeSet(0, pallocChunkPages) {
506+
return false
507+
}
508+
}
509+
chunk = p.chunkOf(ec)
510+
if !chunk.scavenged.isRangeSet(0, ei+1) {
511+
return false
512+
}
513+
return true
514+
}
515+
516+
484517
// update updates heap metadata. It must be called each time the bitmap
485518
// is updated.
486519
//

0 commit comments

Comments
 (0)