]> Cypherpunks.ru repositories - gostls13.git/commitdiff
runtime: use smaller fields for mspan.freeindex and nelems
authorCherry Mui <cherryyz@google.com>
Wed, 16 Nov 2022 22:32:08 +0000 (17:32 -0500)
committerCherry Mui <cherryyz@google.com>
Mon, 2 Oct 2023 20:39:21 +0000 (20:39 +0000)
mspan.freeindex and nelems can fit into uint16 for all possible
values. Use uint16 instead of uintptr.

Change-Id: Ifce20751e81d5022be1f6b5cbb5fbe4fd1728b1b
Reviewed-on: https://go-review.googlesource.com/c/go/+/451359
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/runtime/export_test.go
src/runtime/heapdump.go
src/runtime/malloc.go
src/runtime/mbitmap.go
src/runtime/mcache.go
src/runtime/mcentral.go
src/runtime/mgcsweep.go
src/runtime/mheap.go
src/runtime/pinner.go

index 63764451103964718d7b30614cda760a319ead04..f81e8a9ea1ce0135701feecc136ef07e237d1312 100644 (file)
@@ -1372,7 +1372,7 @@ func FreeMSpan(s *MSpan) {
 
 func MSpanCountAlloc(ms *MSpan, bits []byte) int {
        s := (*mspan)(ms)
-       s.nelems = uintptr(len(bits) * 8)
+       s.nelems = uint16(len(bits) * 8)
        s.gcmarkBits = (*gcBits)(unsafe.Pointer(&bits[0]))
        result := s.countAlloc()
        s.gcmarkBits = nil
index 4283aac320eb3291793037f8f30352e144039146..2394a3e9eb3a1522f2c6021afff7aff59fe00b7c 100644 (file)
@@ -488,8 +488,8 @@ func dumpobjs() {
                        throw("freemark array doesn't have enough entries")
                }
 
-               for freeIndex := uintptr(0); freeIndex < s.nelems; freeIndex++ {
-                       if s.isFree(freeIndex) {
+               for freeIndex := uint16(0); freeIndex < s.nelems; freeIndex++ {
+                       if s.isFree(uintptr(freeIndex)) {
                                freemark[freeIndex] = true
                        }
                }
index ec2e547d3f57ccf6125848db44e9e5e47f7b4327..a071428391eb29d3494dc881b377a2db040872e4 100644 (file)
@@ -886,7 +886,7 @@ var zerobase uintptr
 func nextFreeFast(s *mspan) gclinkptr {
        theBit := sys.TrailingZeros64(s.allocCache) // Is there a free object in the allocCache?
        if theBit < 64 {
-               result := s.freeindex + uintptr(theBit)
+               result := s.freeindex + uint16(theBit)
                if result < s.nelems {
                        freeidx := result + 1
                        if freeidx%64 == 0 && freeidx != s.nelems {
@@ -895,7 +895,7 @@ func nextFreeFast(s *mspan) gclinkptr {
                        s.allocCache >>= uint(theBit + 1)
                        s.freeindex = freeidx
                        s.allocCount++
-                       return gclinkptr(result*s.elemsize + s.base())
+                       return gclinkptr(uintptr(result)*s.elemsize + s.base())
                }
        }
        return 0
@@ -916,7 +916,7 @@ func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, shouldhelpgc bo
        freeIndex := s.nextFreeIndex()
        if freeIndex == s.nelems {
                // The span is full.
-               if uintptr(s.allocCount) != s.nelems {
+               if s.allocCount != s.nelems {
                        println("runtime: s.allocCount=", s.allocCount, "s.nelems=", s.nelems)
                        throw("s.allocCount != s.nelems && freeIndex == s.nelems")
                }
@@ -931,9 +931,9 @@ func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, shouldhelpgc bo
                throw("freeIndex is not valid")
        }
 
-       v = gclinkptr(freeIndex*s.elemsize + s.base())
+       v = gclinkptr(uintptr(freeIndex)*s.elemsize + s.base())
        s.allocCount++
-       if uintptr(s.allocCount) > s.nelems {
+       if s.allocCount > s.nelems {
                println("s.allocCount=", s.allocCount, "s.nelems=", s.nelems)
                throw("s.allocCount > s.nelems")
        }
index 4ba25901d43077bdb0557d645e9e81801eeaa8d4..20323ff82d6731503ea90d5db1ceb78185a0bdc5 100644 (file)
@@ -117,8 +117,8 @@ func (s *mspan) allocBitsForIndex(allocBitIndex uintptr) markBits {
 // and negates them so that ctz (count trailing zeros) instructions
 // can be used. It then places these 8 bytes into the cached 64 bit
 // s.allocCache.
-func (s *mspan) refillAllocCache(whichByte uintptr) {
-       bytes := (*[8]uint8)(unsafe.Pointer(s.allocBits.bytep(whichByte)))
+func (s *mspan) refillAllocCache(whichByte uint16) {
+       bytes := (*[8]uint8)(unsafe.Pointer(s.allocBits.bytep(uintptr(whichByte))))
        aCache := uint64(0)
        aCache |= uint64(bytes[0])
        aCache |= uint64(bytes[1]) << (1 * 8)
@@ -135,7 +135,7 @@ func (s *mspan) refillAllocCache(whichByte uintptr) {
 // or after s.freeindex.
 // There are hardware instructions that can be used to make this
 // faster if profiling warrants it.
-func (s *mspan) nextFreeIndex() uintptr {
+func (s *mspan) nextFreeIndex() uint16 {
        sfreeindex := s.freeindex
        snelems := s.nelems
        if sfreeindex == snelems {
@@ -163,7 +163,7 @@ func (s *mspan) nextFreeIndex() uintptr {
                // nothing available in cached bits
                // grab the next 8 bytes and try again.
        }
-       result := sfreeindex + uintptr(bitIndex)
+       result := sfreeindex + uint16(bitIndex)
        if result >= snelems {
                s.freeindex = snelems
                return snelems
@@ -191,7 +191,7 @@ func (s *mspan) nextFreeIndex() uintptr {
 // been no preemption points since ensuring this (which could allow a
 // GC transition, which would allow the state to change).
 func (s *mspan) isFree(index uintptr) bool {
-       if index < s.freeIndexForScan {
+       if index < uintptr(s.freeIndexForScan) {
                return false
        }
        bytep, mask := s.allocBits.bitp(index)
@@ -751,7 +751,7 @@ func (s *mspan) initHeapBits(forceClear bool) {
 // scanning the allocation bitmap.
 func (s *mspan) countAlloc() int {
        count := 0
-       bytes := divRoundUp(s.nelems, 8)
+       bytes := divRoundUp(uintptr(s.nelems), 8)
        // Iterate over each 8-byte chunk and count allocations
        // with an intrinsic. Note that newMarkBits guarantees that
        // gcmarkBits will be 8-byte aligned, so we don't have to
index 757d09787dba2da3591def826987a50388de8fa2..d4b6eef13ac41fb5b84c893e1eeb1b20fe8fd034 100644 (file)
@@ -148,7 +148,7 @@ func (c *mcache) refill(spc spanClass) {
        // Return the current cached span to the central lists.
        s := c.alloc[spc]
 
-       if uintptr(s.allocCount) != s.nelems {
+       if s.allocCount != s.nelems {
                throw("refill of span with free space remaining")
        }
        if s != &emptymspan {
@@ -184,7 +184,7 @@ func (c *mcache) refill(spc spanClass) {
                throw("out of memory")
        }
 
-       if uintptr(s.allocCount) == s.nelems {
+       if s.allocCount == s.nelems {
                throw("span has no free space")
        }
 
@@ -284,7 +284,7 @@ func (c *mcache) releaseAll() {
                                //
                                // If this span was cached before sweep, then gcController.heapLive was totally
                                // recomputed since caching this span, so we don't do this for stale spans.
-                               dHeapLive -= int64(s.nelems-uintptr(s.allocCount)) * int64(s.elemsize)
+                               dHeapLive -= int64(s.nelems-s.allocCount) * int64(s.elemsize)
                        }
 
                        // Release the span to the mcentral.
index 78611994f341584bf15bd696a13738f9eab20f48..b1dcbd7e7ddc7006c2d08413d82e08d66485a163 100644 (file)
@@ -174,7 +174,7 @@ havespan:
                traceGCSweepDone()
        }
        n := int(s.nelems) - int(s.allocCount)
-       if n == 0 || s.freeindex == s.nelems || uintptr(s.allocCount) == s.nelems {
+       if n == 0 || s.freeindex == s.nelems || s.allocCount == s.nelems {
                throw("span has no free objects")
        }
        freeByteBase := s.freeindex &^ (64 - 1)
index 68f1aae6008055b7ee72c1f8539ae4b5be0cdbe1..986eb573ca42ab7205f45e222c0e6e8453f13c32 100644 (file)
@@ -602,8 +602,8 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
                // efficient; allocfreetrace has massive overhead.
                mbits := s.markBitsForBase()
                abits := s.allocBitsForIndex(0)
-               for i := uintptr(0); i < s.nelems; i++ {
-                       if !mbits.isMarked() && (abits.index < s.freeindex || abits.isMarked()) {
+               for i := uintptr(0); i < uintptr(s.nelems); i++ {
+                       if !mbits.isMarked() && (abits.index < uintptr(s.freeindex) || abits.isMarked()) {
                                x := s.base() + i*s.elemsize
                                if debug.allocfreetrace != 0 {
                                        tracefree(unsafe.Pointer(x), size)
@@ -634,12 +634,12 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
                //
                // Check the first bitmap byte, where we have to be
                // careful with freeindex.
-               obj := s.freeindex
+               obj := uintptr(s.freeindex)
                if (*s.gcmarkBits.bytep(obj / 8)&^*s.allocBits.bytep(obj / 8))>>(obj%8) != 0 {
                        s.reportZombies()
                }
                // Check remaining bytes.
-               for i := obj/8 + 1; i < divRoundUp(s.nelems, 8); i++ {
+               for i := obj/8 + 1; i < divRoundUp(uintptr(s.nelems), 8); i++ {
                        if *s.gcmarkBits.bytep(i)&^*s.allocBits.bytep(i) != 0 {
                                s.reportZombies()
                        }
@@ -666,7 +666,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
        // gcmarkBits becomes the allocBits.
        // get a fresh cleared gcmarkBits in preparation for next GC
        s.allocBits = s.gcmarkBits
-       s.gcmarkBits = newMarkBits(s.nelems)
+       s.gcmarkBits = newMarkBits(uintptr(s.nelems))
 
        // refresh pinnerBits if they exists
        if s.pinnerBits != nil {
@@ -760,7 +760,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
                                return true
                        }
                        // Return span back to the right mcentral list.
-                       if uintptr(nalloc) == s.nelems {
+                       if nalloc == s.nelems {
                                mheap_.central[spc].mcentral.fullSwept(sweepgen).push(s)
                        } else {
                                mheap_.central[spc].mcentral.partialSwept(sweepgen).push(s)
@@ -829,10 +829,10 @@ func (s *mspan) reportZombies() {
        print("runtime: marked free object in span ", s, ", elemsize=", s.elemsize, " freeindex=", s.freeindex, " (bad use of unsafe.Pointer? try -d=checkptr)\n")
        mbits := s.markBitsForBase()
        abits := s.allocBitsForIndex(0)
-       for i := uintptr(0); i < s.nelems; i++ {
+       for i := uintptr(0); i < uintptr(s.nelems); i++ {
                addr := s.base() + i*s.elemsize
                print(hex(addr))
-               alloc := i < s.freeindex || abits.isMarked()
+               alloc := i < uintptr(s.freeindex) || abits.isMarked()
                if alloc {
                        print(" alloc")
                } else {
index 0ba45009eb34c85457febc8301cc4d03e79a4fa3..42318ca04cce8284ec9f07adad439f112f56d91c 100644 (file)
@@ -435,10 +435,17 @@ type mspan struct {
        // undefined and should never be referenced.
        //
        // Object n starts at address n*elemsize + (start << pageShift).
-       freeindex uintptr
+       freeindex uint16
        // TODO: Look up nelems from sizeclass and remove this field if it
        // helps performance.
-       nelems uintptr // number of object in the span.
+       nelems uint16 // number of object in the span.
+       // freeIndexForScan is like freeindex, except that freeindex is
+       // used by the allocator whereas freeIndexForScan is used by the
+       // GC scanner. They are two fields so that the GC sees the object
+       // is allocated only when the object and the heap bits are
+       // initialized (see also the assignment of freeIndexForScan in
+       // mallocgc, and issue 54596).
+       freeIndexForScan uint16
 
        // Cache of the allocBits at freeindex. allocCache is shifted
        // such that the lowest bit corresponds to the bit freeindex.
@@ -495,14 +502,6 @@ type mspan struct {
        speciallock           mutex         // guards specials list and changes to pinnerBits
        specials              *special      // linked list of special records sorted by offset.
        userArenaChunkFree    addrRange     // interval for managing chunk allocation
-
-       // freeIndexForScan is like freeindex, except that freeindex is
-       // used by the allocator whereas freeIndexForScan is used by the
-       // GC scanner. They are two fields so that the GC sees the object
-       // is allocated only when the object and the heap bits are
-       // initialized (see also the assignment of freeIndexForScan in
-       // mallocgc, and issue 54596).
-       freeIndexForScan uintptr
 }
 
 func (s *mspan) base() uintptr {
@@ -1403,7 +1402,7 @@ func (h *mheap) initSpan(s *mspan, typ spanAllocType, spanclass spanClass, base,
                        s.divMul = 0
                } else {
                        s.elemsize = uintptr(class_to_size[sizeclass])
-                       s.nelems = nbytes / s.elemsize
+                       s.nelems = uint16(nbytes / s.elemsize)
                        s.divMul = class_to_divmagic[sizeclass]
                }
 
@@ -1411,8 +1410,8 @@ func (h *mheap) initSpan(s *mspan, typ spanAllocType, spanclass spanClass, base,
                s.freeindex = 0
                s.freeIndexForScan = 0
                s.allocCache = ^uint64(0) // all 1s indicating all free.
-               s.gcmarkBits = newMarkBits(s.nelems)
-               s.allocBits = newAllocBits(s.nelems)
+               s.gcmarkBits = newMarkBits(uintptr(s.nelems))
+               s.allocBits = newAllocBits(uintptr(s.nelems))
 
                // It's safe to access h.sweepgen without the heap lock because it's
                // only ever updated with the world stopped and we run on the
index c0db91eba80f13fdcd37dd906a7f30fdd889ef17..0cc48e2164772a9b60c5b6fed248901f2bfb593d 100644 (file)
@@ -267,14 +267,14 @@ func (p *pinnerBits) ofObject(n uintptr) pinState {
 }
 
 func (s *mspan) pinnerBitSize() uintptr {
-       return divRoundUp(s.nelems*2, 8)
+       return divRoundUp(uintptr(s.nelems)*2, 8)
 }
 
 // newPinnerBits returns a pointer to 8 byte aligned bytes to be used for this
 // span's pinner bits. newPinneBits is used to mark objects that are pinned.
 // They are copied when the span is swept.
 func (s *mspan) newPinnerBits() *pinnerBits {
-       return (*pinnerBits)(newMarkBits(s.nelems * 2))
+       return (*pinnerBits)(newMarkBits(uintptr(s.nelems) * 2))
 }
 
 // nosplit, because it's called by isPinned, which is nosplit