Commit 1a033b1a authored by Austin Clements's avatar Austin Clements

runtime: separate spans of noscan objects

Currently, we mix objects with pointers and objects without pointers
("noscan" objects) together in memory. As a result, for every object
we grey, we have to check that object's heap bits to find out if it's
noscan, which adds to the per-object cost of GC. This also hurts the
TLB footprint of the garbage collector because it decreases the
density of scannable objects at the page level.

This commit improves the situation by using separate spans for noscan
objects. This will allow a much simpler noscan check (in a follow up
CL), eliminate the need to clear the bitmap of noscan objects (in a
follow up CL), and improves TLB footprint by increasing the density of
scannable objects.

This is also a step toward eliminating dead bits, since the current
noscan check depends on checking the dead bit of the first word.

This has no effect on the heap size of the garbage benchmark.

We'll measure the performance change of this after the follow-up
optimizations.

This is a cherry-pick from dev.garbage commit d491e550. The only
non-trivial merge conflict was in updatememstats in mstats.go, where
we now have to separate the per-spanclass stats from the per-sizeclass
stats.

Change-Id: I13bdc4869538ece5649a8d2a41c6605371618e40
Reviewed-on: https://go-review.googlesource.com/41251
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRick Hudson <rlh@golang.org>
parent 390fdead
...@@ -300,13 +300,13 @@ func ReadMemStatsSlow() (base, slow MemStats) { ...@@ -300,13 +300,13 @@ func ReadMemStatsSlow() (base, slow MemStats) {
if s.state != mSpanInUse { if s.state != mSpanInUse {
continue continue
} }
if s.sizeclass == 0 { if sizeclass := s.spanclass.sizeclass(); sizeclass == 0 {
slow.Mallocs++ slow.Mallocs++
slow.Alloc += uint64(s.elemsize) slow.Alloc += uint64(s.elemsize)
} else { } else {
slow.Mallocs += uint64(s.allocCount) slow.Mallocs += uint64(s.allocCount)
slow.Alloc += uint64(s.allocCount) * uint64(s.elemsize) slow.Alloc += uint64(s.allocCount) * uint64(s.elemsize)
bySize[s.sizeclass].Mallocs += uint64(s.allocCount) bySize[sizeclass].Mallocs += uint64(s.allocCount)
} }
} }
......
...@@ -518,8 +518,8 @@ func nextFreeFast(s *mspan) gclinkptr { ...@@ -518,8 +518,8 @@ func nextFreeFast(s *mspan) gclinkptr {
// weight allocation. If it is a heavy weight allocation the caller must // weight allocation. If it is a heavy weight allocation the caller must
// determine whether a new GC cycle needs to be started or if the GC is active // determine whether a new GC cycle needs to be started or if the GC is active
// whether this goroutine needs to assist the GC. // whether this goroutine needs to assist the GC.
func (c *mcache) nextFree(sizeclass uint8) (v gclinkptr, s *mspan, shouldhelpgc bool) { func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, shouldhelpgc bool) {
s = c.alloc[sizeclass] s = c.alloc[spc]
shouldhelpgc = false shouldhelpgc = false
freeIndex := s.nextFreeIndex() freeIndex := s.nextFreeIndex()
if freeIndex == s.nelems { if freeIndex == s.nelems {
...@@ -529,10 +529,10 @@ func (c *mcache) nextFree(sizeclass uint8) (v gclinkptr, s *mspan, shouldhelpgc ...@@ -529,10 +529,10 @@ func (c *mcache) nextFree(sizeclass uint8) (v gclinkptr, s *mspan, shouldhelpgc
throw("s.allocCount != s.nelems && freeIndex == s.nelems") throw("s.allocCount != s.nelems && freeIndex == s.nelems")
} }
systemstack(func() { systemstack(func() {
c.refill(int32(sizeclass)) c.refill(spc)
}) })
shouldhelpgc = true shouldhelpgc = true
s = c.alloc[sizeclass] s = c.alloc[spc]
freeIndex = s.nextFreeIndex() freeIndex = s.nextFreeIndex()
} }
...@@ -656,10 +656,10 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { ...@@ -656,10 +656,10 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
return x return x
} }
// Allocate a new maxTinySize block. // Allocate a new maxTinySize block.
span := c.alloc[tinySizeClass] span := c.alloc[tinySpanClass]
v := nextFreeFast(span) v := nextFreeFast(span)
if v == 0 { if v == 0 {
v, _, shouldhelpgc = c.nextFree(tinySizeClass) v, _, shouldhelpgc = c.nextFree(tinySpanClass)
} }
x = unsafe.Pointer(v) x = unsafe.Pointer(v)
(*[2]uint64)(x)[0] = 0 (*[2]uint64)(x)[0] = 0
...@@ -679,10 +679,11 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { ...@@ -679,10 +679,11 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
sizeclass = size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv] sizeclass = size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv]
} }
size = uintptr(class_to_size[sizeclass]) size = uintptr(class_to_size[sizeclass])
span := c.alloc[sizeclass] spc := makeSpanClass(sizeclass, noscan)
span := c.alloc[spc]
v := nextFreeFast(span) v := nextFreeFast(span)
if v == 0 { if v == 0 {
v, span, shouldhelpgc = c.nextFree(sizeclass) v, span, shouldhelpgc = c.nextFree(spc)
} }
x = unsafe.Pointer(v) x = unsafe.Pointer(v)
if needzero && span.needzero != 0 { if needzero && span.needzero != 0 {
...@@ -693,7 +694,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { ...@@ -693,7 +694,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
var s *mspan var s *mspan
shouldhelpgc = true shouldhelpgc = true
systemstack(func() { systemstack(func() {
s = largeAlloc(size, needzero) s = largeAlloc(size, needzero, noscan)
}) })
s.freeindex = 1 s.freeindex = 1
s.allocCount = 1 s.allocCount = 1
...@@ -784,7 +785,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer { ...@@ -784,7 +785,7 @@ func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer {
return x return x
} }
func largeAlloc(size uintptr, needzero bool) *mspan { func largeAlloc(size uintptr, needzero bool, noscan bool) *mspan {
// print("largeAlloc size=", size, "\n") // print("largeAlloc size=", size, "\n")
if size+_PageSize < size { if size+_PageSize < size {
...@@ -800,7 +801,7 @@ func largeAlloc(size uintptr, needzero bool) *mspan { ...@@ -800,7 +801,7 @@ func largeAlloc(size uintptr, needzero bool) *mspan {
// pays the debt down to npage pages. // pays the debt down to npage pages.
deductSweepCredit(npages*_PageSize, npages) deductSweepCredit(npages*_PageSize, npages)
s := mheap_.alloc(npages, 0, true, needzero) s := mheap_.alloc(npages, makeSpanClass(0, noscan), true, needzero)
if s == nil { if s == nil {
throw("out of memory") throw("out of memory")
} }
......
...@@ -499,6 +499,7 @@ func (h heapBits) isPointer() bool { ...@@ -499,6 +499,7 @@ func (h heapBits) isPointer() bool {
// It must be told how large the object at h is for efficiency. // It must be told how large the object at h is for efficiency.
// h must describe the initial word of the object. // h must describe the initial word of the object.
func (h heapBits) hasPointers(size uintptr) bool { func (h heapBits) hasPointers(size uintptr) bool {
// TODO: Use span.noScan instead of the heap bitmap.
if size == sys.PtrSize { // 1-word objects are always pointers if size == sys.PtrSize { // 1-word objects are always pointers
return true return true
} }
......
...@@ -33,7 +33,8 @@ type mcache struct { ...@@ -33,7 +33,8 @@ type mcache struct {
local_tinyallocs uintptr // number of tiny allocs not counted in other stats local_tinyallocs uintptr // number of tiny allocs not counted in other stats
// The rest is not accessed on every malloc. // The rest is not accessed on every malloc.
alloc [_NumSizeClasses]*mspan // spans to allocate from
alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass
stackcache [_NumStackOrders]stackfreelist stackcache [_NumStackOrders]stackfreelist
...@@ -77,7 +78,7 @@ func allocmcache() *mcache { ...@@ -77,7 +78,7 @@ func allocmcache() *mcache {
lock(&mheap_.lock) lock(&mheap_.lock)
c := (*mcache)(mheap_.cachealloc.alloc()) c := (*mcache)(mheap_.cachealloc.alloc())
unlock(&mheap_.lock) unlock(&mheap_.lock)
for i := 0; i < _NumSizeClasses; i++ { for i := range c.alloc {
c.alloc[i] = &emptymspan c.alloc[i] = &emptymspan
} }
c.next_sample = nextSample() c.next_sample = nextSample()
...@@ -103,12 +104,12 @@ func freemcache(c *mcache) { ...@@ -103,12 +104,12 @@ func freemcache(c *mcache) {
// Gets a span that has a free object in it and assigns it // Gets a span that has a free object in it and assigns it
// to be the cached span for the given sizeclass. Returns this span. // to be the cached span for the given sizeclass. Returns this span.
func (c *mcache) refill(sizeclass int32) *mspan { func (c *mcache) refill(spc spanClass) *mspan {
_g_ := getg() _g_ := getg()
_g_.m.locks++ _g_.m.locks++
// Return the current cached span to the central lists. // Return the current cached span to the central lists.
s := c.alloc[sizeclass] s := c.alloc[spc]
if uintptr(s.allocCount) != s.nelems { if uintptr(s.allocCount) != s.nelems {
throw("refill of span with free space remaining") throw("refill of span with free space remaining")
...@@ -119,7 +120,7 @@ func (c *mcache) refill(sizeclass int32) *mspan { ...@@ -119,7 +120,7 @@ func (c *mcache) refill(sizeclass int32) *mspan {
} }
// Get a new cached span from the central lists. // Get a new cached span from the central lists.
s = mheap_.central[sizeclass].mcentral.cacheSpan() s = mheap_.central[spc].mcentral.cacheSpan()
if s == nil { if s == nil {
throw("out of memory") throw("out of memory")
} }
...@@ -128,13 +129,13 @@ func (c *mcache) refill(sizeclass int32) *mspan { ...@@ -128,13 +129,13 @@ func (c *mcache) refill(sizeclass int32) *mspan {
throw("span has no free space") throw("span has no free space")
} }
c.alloc[sizeclass] = s c.alloc[spc] = s
_g_.m.locks-- _g_.m.locks--
return s return s
} }
func (c *mcache) releaseAll() { func (c *mcache) releaseAll() {
for i := 0; i < _NumSizeClasses; i++ { for i := range c.alloc {
s := c.alloc[i] s := c.alloc[i]
if s != &emptymspan { if s != &emptymspan {
mheap_.central[i].mcentral.uncacheSpan(s) mheap_.central[i].mcentral.uncacheSpan(s)
......
...@@ -19,7 +19,7 @@ import "runtime/internal/atomic" ...@@ -19,7 +19,7 @@ import "runtime/internal/atomic"
//go:notinheap //go:notinheap
type mcentral struct { type mcentral struct {
lock mutex lock mutex
sizeclass int32 spanclass spanClass
nonempty mSpanList // list of spans with a free object, ie a nonempty free list nonempty mSpanList // list of spans with a free object, ie a nonempty free list
empty mSpanList // list of spans with no free objects (or cached in an mcache) empty mSpanList // list of spans with no free objects (or cached in an mcache)
...@@ -30,8 +30,8 @@ type mcentral struct { ...@@ -30,8 +30,8 @@ type mcentral struct {
} }
// Initialize a single central free list. // Initialize a single central free list.
func (c *mcentral) init(sizeclass int32) { func (c *mcentral) init(spc spanClass) {
c.sizeclass = sizeclass c.spanclass = spc
c.nonempty.init() c.nonempty.init()
c.empty.init() c.empty.init()
} }
...@@ -39,7 +39,7 @@ func (c *mcentral) init(sizeclass int32) { ...@@ -39,7 +39,7 @@ func (c *mcentral) init(sizeclass int32) {
// Allocate a span to use in an MCache. // Allocate a span to use in an MCache.
func (c *mcentral) cacheSpan() *mspan { func (c *mcentral) cacheSpan() *mspan {
// Deduct credit for this span allocation and sweep if necessary. // Deduct credit for this span allocation and sweep if necessary.
spanBytes := uintptr(class_to_allocnpages[c.sizeclass]) * _PageSize spanBytes := uintptr(class_to_allocnpages[c.spanclass.sizeclass()]) * _PageSize
deductSweepCredit(spanBytes, 0) deductSweepCredit(spanBytes, 0)
lock(&c.lock) lock(&c.lock)
...@@ -225,11 +225,11 @@ func (c *mcentral) freeSpan(s *mspan, preserve bool, wasempty bool) bool { ...@@ -225,11 +225,11 @@ func (c *mcentral) freeSpan(s *mspan, preserve bool, wasempty bool) bool {
// grow allocates a new empty span from the heap and initializes it for c's size class. // grow allocates a new empty span from the heap and initializes it for c's size class.
func (c *mcentral) grow() *mspan { func (c *mcentral) grow() *mspan {
npages := uintptr(class_to_allocnpages[c.sizeclass]) npages := uintptr(class_to_allocnpages[c.spanclass.sizeclass()])
size := uintptr(class_to_size[c.sizeclass]) size := uintptr(class_to_size[c.spanclass.sizeclass()])
n := (npages << _PageShift) / size n := (npages << _PageShift) / size
s := mheap_.alloc(npages, c.sizeclass, false, true) s := mheap_.alloc(npages, c.spanclass, false, true)
if s == nil { if s == nil {
return nil return nil
} }
......
...@@ -455,7 +455,7 @@ func findObject(v unsafe.Pointer) (s *mspan, x unsafe.Pointer, n uintptr) { ...@@ -455,7 +455,7 @@ func findObject(v unsafe.Pointer) (s *mspan, x unsafe.Pointer, n uintptr) {
} }
n = s.elemsize n = s.elemsize
if s.sizeclass != 0 { if s.spanclass.sizeclass() != 0 {
x = add(x, (uintptr(v)-uintptr(x))/n*n) x = add(x, (uintptr(v)-uintptr(x))/n*n)
} }
return return
......
...@@ -1290,7 +1290,7 @@ func gcDumpObject(label string, obj, off uintptr) { ...@@ -1290,7 +1290,7 @@ func gcDumpObject(label string, obj, off uintptr) {
print(" s=nil\n") print(" s=nil\n")
return return
} }
print(" s.base()=", hex(s.base()), " s.limit=", hex(s.limit), " s.sizeclass=", s.sizeclass, " s.elemsize=", s.elemsize, " s.state=") print(" s.base()=", hex(s.base()), " s.limit=", hex(s.limit), " s.spanclass=", s.spanclass, " s.elemsize=", s.elemsize, " s.state=")
if 0 <= s.state && int(s.state) < len(mSpanStateNames) { if 0 <= s.state && int(s.state) < len(mSpanStateNames) {
print(mSpanStateNames[s.state], "\n") print(mSpanStateNames[s.state], "\n")
} else { } else {
......
...@@ -195,7 +195,7 @@ func (s *mspan) sweep(preserve bool) bool { ...@@ -195,7 +195,7 @@ func (s *mspan) sweep(preserve bool) bool {
atomic.Xadd64(&mheap_.pagesSwept, int64(s.npages)) atomic.Xadd64(&mheap_.pagesSwept, int64(s.npages))
cl := s.sizeclass spc := s.spanclass
size := s.elemsize size := s.elemsize
res := false res := false
...@@ -288,7 +288,7 @@ func (s *mspan) sweep(preserve bool) bool { ...@@ -288,7 +288,7 @@ func (s *mspan) sweep(preserve bool) bool {
// Count the number of free objects in this span. // Count the number of free objects in this span.
nalloc := uint16(s.countAlloc()) nalloc := uint16(s.countAlloc())
if cl == 0 && nalloc == 0 { if spc.sizeclass() == 0 && nalloc == 0 {
s.needzero = 1 s.needzero = 1
freeToHeap = true freeToHeap = true
} }
...@@ -331,9 +331,9 @@ func (s *mspan) sweep(preserve bool) bool { ...@@ -331,9 +331,9 @@ func (s *mspan) sweep(preserve bool) bool {
atomic.Store(&s.sweepgen, sweepgen) atomic.Store(&s.sweepgen, sweepgen)
} }
if nfreed > 0 && cl != 0 { if nfreed > 0 && spc.sizeclass() != 0 {
c.local_nsmallfree[cl] += uintptr(nfreed) c.local_nsmallfree[spc.sizeclass()] += uintptr(nfreed)
res = mheap_.central[cl].mcentral.freeSpan(s, preserve, wasempty) res = mheap_.central[spc].mcentral.freeSpan(s, preserve, wasempty)
// MCentral_FreeSpan updates sweepgen // MCentral_FreeSpan updates sweepgen
} else if freeToHeap { } else if freeToHeap {
// Free large span to heap // Free large span to heap
......
...@@ -119,7 +119,8 @@ type mheap struct { ...@@ -119,7 +119,8 @@ type mheap struct {
// the padding makes sure that the MCentrals are // the padding makes sure that the MCentrals are
// spaced CacheLineSize bytes apart, so that each MCentral.lock // spaced CacheLineSize bytes apart, so that each MCentral.lock
// gets its own cache line. // gets its own cache line.
central [_NumSizeClasses]struct { // central is indexed by spanClass.
central [numSpanClasses]struct {
mcentral mcentral mcentral mcentral
pad [sys.CacheLineSize - unsafe.Sizeof(mcentral{})%sys.CacheLineSize]byte pad [sys.CacheLineSize - unsafe.Sizeof(mcentral{})%sys.CacheLineSize]byte
} }
...@@ -260,7 +261,7 @@ type mspan struct { ...@@ -260,7 +261,7 @@ type mspan struct {
divMul uint16 // for divide by elemsize - divMagic.mul divMul uint16 // for divide by elemsize - divMagic.mul
baseMask uint16 // if non-0, elemsize is a power of 2, & this will get object allocation base baseMask uint16 // if non-0, elemsize is a power of 2, & this will get object allocation base
allocCount uint16 // number of allocated objects allocCount uint16 // number of allocated objects
sizeclass uint8 // size class spanclass spanClass // size class and noscan (uint8)
incache bool // being used by an mcache incache bool // being used by an mcache
state mSpanState // mspaninuse etc state mSpanState // mspaninuse etc
needzero uint8 // needs to be zeroed before allocation needzero uint8 // needs to be zeroed before allocation
...@@ -315,6 +316,31 @@ func recordspan(vh unsafe.Pointer, p unsafe.Pointer) { ...@@ -315,6 +316,31 @@ func recordspan(vh unsafe.Pointer, p unsafe.Pointer) {
h.allspans = append(h.allspans, s) h.allspans = append(h.allspans, s)
} }
// A spanClass represents the size class and noscan-ness of a span.
//
// Each size class has a noscan spanClass and a scan spanClass. The
// noscan spanClass contains only noscan objects, which do not contain
// pointers and thus do not need to be scanned by the garbage
// collector.
type spanClass uint8
const (
numSpanClasses = _NumSizeClasses << 1
tinySpanClass = tinySizeClass<<1 | 1
)
func makeSpanClass(sizeclass uint8, noscan bool) spanClass {
return spanClass(sizeclass<<1) | spanClass(bool2int(noscan))
}
func (sc spanClass) sizeclass() int8 {
return int8(sc >> 1)
}
func (sc spanClass) noscan() bool {
return sc&1 != 0
}
// inheap reports whether b is a pointer into a (potentially dead) heap object. // inheap reports whether b is a pointer into a (potentially dead) heap object.
// It returns false for pointers into _MSpanManual spans. // It returns false for pointers into _MSpanManual spans.
// Non-preemptible because it is used by write barriers. // Non-preemptible because it is used by write barriers.
...@@ -399,7 +425,7 @@ func mlookup(v uintptr, base *uintptr, size *uintptr, sp **mspan) int32 { ...@@ -399,7 +425,7 @@ func mlookup(v uintptr, base *uintptr, size *uintptr, sp **mspan) int32 {
} }
p := s.base() p := s.base()
if s.sizeclass == 0 { if s.spanclass.sizeclass() == 0 {
// Large object. // Large object.
if base != nil { if base != nil {
*base = p *base = p
...@@ -447,7 +473,7 @@ func (h *mheap) init(spansStart, spansBytes uintptr) { ...@@ -447,7 +473,7 @@ func (h *mheap) init(spansStart, spansBytes uintptr) {
h.busylarge.init() h.busylarge.init()
for i := range h.central { for i := range h.central {
h.central[i].mcentral.init(int32(i)) h.central[i].mcentral.init(spanClass(i))
} }
sp := (*slice)(unsafe.Pointer(&h.spans)) sp := (*slice)(unsafe.Pointer(&h.spans))
...@@ -577,7 +603,7 @@ func (h *mheap) reclaim(npage uintptr) { ...@@ -577,7 +603,7 @@ func (h *mheap) reclaim(npage uintptr) {
// Allocate a new span of npage pages from the heap for GC'd memory // Allocate a new span of npage pages from the heap for GC'd memory
// and record its size class in the HeapMap and HeapMapCache. // and record its size class in the HeapMap and HeapMapCache.
func (h *mheap) alloc_m(npage uintptr, sizeclass int32, large bool) *mspan { func (h *mheap) alloc_m(npage uintptr, spanclass spanClass, large bool) *mspan {
_g_ := getg() _g_ := getg()
if _g_ != _g_.m.g0 { if _g_ != _g_.m.g0 {
throw("_mheap_alloc not on g0 stack") throw("_mheap_alloc not on g0 stack")
...@@ -617,8 +643,8 @@ func (h *mheap) alloc_m(npage uintptr, sizeclass int32, large bool) *mspan { ...@@ -617,8 +643,8 @@ func (h *mheap) alloc_m(npage uintptr, sizeclass int32, large bool) *mspan {
h.sweepSpans[h.sweepgen/2%2].push(s) // Add to swept in-use list. h.sweepSpans[h.sweepgen/2%2].push(s) // Add to swept in-use list.
s.state = _MSpanInUse s.state = _MSpanInUse
s.allocCount = 0 s.allocCount = 0
s.sizeclass = uint8(sizeclass) s.spanclass = spanclass
if sizeclass == 0 { if sizeclass := spanclass.sizeclass(); sizeclass == 0 {
s.elemsize = s.npages << _PageShift s.elemsize = s.npages << _PageShift
s.divShift = 0 s.divShift = 0
s.divMul = 0 s.divMul = 0
...@@ -670,13 +696,13 @@ func (h *mheap) alloc_m(npage uintptr, sizeclass int32, large bool) *mspan { ...@@ -670,13 +696,13 @@ func (h *mheap) alloc_m(npage uintptr, sizeclass int32, large bool) *mspan {
return s return s
} }
func (h *mheap) alloc(npage uintptr, sizeclass int32, large bool, needzero bool) *mspan { func (h *mheap) alloc(npage uintptr, spanclass spanClass, large bool, needzero bool) *mspan {
// Don't do any operations that lock the heap on the G stack. // Don't do any operations that lock the heap on the G stack.
// It might trigger stack growth, and the stack growth code needs // It might trigger stack growth, and the stack growth code needs
// to be able to allocate heap. // to be able to allocate heap.
var s *mspan var s *mspan
systemstack(func() { systemstack(func() {
s = h.alloc_m(npage, sizeclass, large) s = h.alloc_m(npage, spanclass, large)
}) })
if s != nil { if s != nil {
...@@ -710,7 +736,7 @@ func (h *mheap) allocManual(npage uintptr, stat *uint64) *mspan { ...@@ -710,7 +736,7 @@ func (h *mheap) allocManual(npage uintptr, stat *uint64) *mspan {
s.state = _MSpanManual s.state = _MSpanManual
s.manualFreeList = 0 s.manualFreeList = 0
s.allocCount = 0 s.allocCount = 0
s.sizeclass = 0 s.spanclass = 0
s.nelems = 0 s.nelems = 0
s.elemsize = 0 s.elemsize = 0
s.limit = s.base() + s.npages<<_PageShift s.limit = s.base() + s.npages<<_PageShift
...@@ -1144,7 +1170,7 @@ func (span *mspan) init(base uintptr, npages uintptr) { ...@@ -1144,7 +1170,7 @@ func (span *mspan) init(base uintptr, npages uintptr) {
span.startAddr = base span.startAddr = base
span.npages = npages span.npages = npages
span.allocCount = 0 span.allocCount = 0
span.sizeclass = 0 span.spanclass = 0
span.incache = false span.incache = false
span.elemsize = 0 span.elemsize = 0
span.state = _MSpanDead span.state = _MSpanDead
......
...@@ -552,7 +552,18 @@ func updatememstats() { ...@@ -552,7 +552,18 @@ func updatememstats() {
// Collect allocation stats. This is safe and consistent // Collect allocation stats. This is safe and consistent
// because the world is stopped. // because the world is stopped.
var smallFree, totalAlloc, totalFree uint64 var smallFree, totalAlloc, totalFree uint64
for i := range mheap_.central { // Collect per-spanclass stats.
for spc := range mheap_.central {
// The mcaches are now empty, so mcentral stats are
// up-to-date.
c := &mheap_.central[spc].mcentral
memstats.nmalloc += c.nmalloc
i := spanClass(spc).sizeclass()
memstats.by_size[i].nmalloc += c.nmalloc
totalAlloc += c.nmalloc * uint64(class_to_size[i])
}
// Collect per-sizeclass stats.
for i := 0; i < _NumSizeClasses; i++ {
if i == 0 { if i == 0 {
memstats.nmalloc += mheap_.nlargealloc memstats.nmalloc += mheap_.nlargealloc
totalAlloc += mheap_.largealloc totalAlloc += mheap_.largealloc
...@@ -560,12 +571,6 @@ func updatememstats() { ...@@ -560,12 +571,6 @@ func updatememstats() {
memstats.nfree += mheap_.nlargefree memstats.nfree += mheap_.nlargefree
continue continue
} }
// The mcaches are now empty, so mcentral stats are
// up-to-date.
c := &mheap_.central[i].mcentral
memstats.nmalloc += c.nmalloc
memstats.by_size[i].nmalloc += c.nmalloc
totalAlloc += c.nmalloc * uint64(class_to_size[i])
// The mcache stats have been flushed to mheap_. // The mcache stats have been flushed to mheap_.
memstats.nfree += mheap_.nsmallfree[i] memstats.nfree += mheap_.nsmallfree[i]
......
...@@ -291,3 +291,10 @@ func checkASM() bool ...@@ -291,3 +291,10 @@ func checkASM() bool
func memequal_varlen(a, b unsafe.Pointer) bool func memequal_varlen(a, b unsafe.Pointer) bool
func eqstring(s1, s2 string) bool func eqstring(s1, s2 string) bool
// bool2int returns 0 if x is false or 1 if x is true.
func bool2int(x bool) int {
// Avoid branches. In the SSA compiler, this compiles to
// exactly what you would want it to.
return int(uint8(*(*uint8)(unsafe.Pointer(&x))))
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment