Skip to content

Commit 340a4f5

Browse files
committed
runtime: use smaller fields for mspan.freeindex and nelems
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 <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 32b6d2d commit 340a4f5

File tree

9 files changed

+40
-41
lines changed

9 files changed

+40
-41
lines changed

src/runtime/export_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1372,7 +1372,7 @@ func FreeMSpan(s *MSpan) {
13721372

13731373
func MSpanCountAlloc(ms *MSpan, bits []byte) int {
13741374
s := (*mspan)(ms)
1375-
s.nelems = uintptr(len(bits) * 8)
1375+
s.nelems = uint16(len(bits) * 8)
13761376
s.gcmarkBits = (*gcBits)(unsafe.Pointer(&bits[0]))
13771377
result := s.countAlloc()
13781378
s.gcmarkBits = nil

src/runtime/heapdump.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -488,8 +488,8 @@ func dumpobjs() {
488488
throw("freemark array doesn't have enough entries")
489489
}
490490

491-
for freeIndex := uintptr(0); freeIndex < s.nelems; freeIndex++ {
492-
if s.isFree(freeIndex) {
491+
for freeIndex := uint16(0); freeIndex < s.nelems; freeIndex++ {
492+
if s.isFree(uintptr(freeIndex)) {
493493
freemark[freeIndex] = true
494494
}
495495
}

src/runtime/malloc.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,7 @@ var zerobase uintptr
886886
func nextFreeFast(s *mspan) gclinkptr {
887887
theBit := sys.TrailingZeros64(s.allocCache) // Is there a free object in the allocCache?
888888
if theBit < 64 {
889-
result := s.freeindex + uintptr(theBit)
889+
result := s.freeindex + uint16(theBit)
890890
if result < s.nelems {
891891
freeidx := result + 1
892892
if freeidx%64 == 0 && freeidx != s.nelems {
@@ -895,7 +895,7 @@ func nextFreeFast(s *mspan) gclinkptr {
895895
s.allocCache >>= uint(theBit + 1)
896896
s.freeindex = freeidx
897897
s.allocCount++
898-
return gclinkptr(result*s.elemsize + s.base())
898+
return gclinkptr(uintptr(result)*s.elemsize + s.base())
899899
}
900900
}
901901
return 0
@@ -916,7 +916,7 @@ func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, shouldhelpgc bo
916916
freeIndex := s.nextFreeIndex()
917917
if freeIndex == s.nelems {
918918
// The span is full.
919-
if uintptr(s.allocCount) != s.nelems {
919+
if s.allocCount != s.nelems {
920920
println("runtime: s.allocCount=", s.allocCount, "s.nelems=", s.nelems)
921921
throw("s.allocCount != s.nelems && freeIndex == s.nelems")
922922
}
@@ -931,9 +931,9 @@ func (c *mcache) nextFree(spc spanClass) (v gclinkptr, s *mspan, shouldhelpgc bo
931931
throw("freeIndex is not valid")
932932
}
933933

934-
v = gclinkptr(freeIndex*s.elemsize + s.base())
934+
v = gclinkptr(uintptr(freeIndex)*s.elemsize + s.base())
935935
s.allocCount++
936-
if uintptr(s.allocCount) > s.nelems {
936+
if s.allocCount > s.nelems {
937937
println("s.allocCount=", s.allocCount, "s.nelems=", s.nelems)
938938
throw("s.allocCount > s.nelems")
939939
}

src/runtime/mbitmap.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ func (s *mspan) allocBitsForIndex(allocBitIndex uintptr) markBits {
117117
// and negates them so that ctz (count trailing zeros) instructions
118118
// can be used. It then places these 8 bytes into the cached 64 bit
119119
// s.allocCache.
120-
func (s *mspan) refillAllocCache(whichByte uintptr) {
121-
bytes := (*[8]uint8)(unsafe.Pointer(s.allocBits.bytep(whichByte)))
120+
func (s *mspan) refillAllocCache(whichByte uint16) {
121+
bytes := (*[8]uint8)(unsafe.Pointer(s.allocBits.bytep(uintptr(whichByte))))
122122
aCache := uint64(0)
123123
aCache |= uint64(bytes[0])
124124
aCache |= uint64(bytes[1]) << (1 * 8)
@@ -135,7 +135,7 @@ func (s *mspan) refillAllocCache(whichByte uintptr) {
135135
// or after s.freeindex.
136136
// There are hardware instructions that can be used to make this
137137
// faster if profiling warrants it.
138-
func (s *mspan) nextFreeIndex() uintptr {
138+
func (s *mspan) nextFreeIndex() uint16 {
139139
sfreeindex := s.freeindex
140140
snelems := s.nelems
141141
if sfreeindex == snelems {
@@ -163,7 +163,7 @@ func (s *mspan) nextFreeIndex() uintptr {
163163
// nothing available in cached bits
164164
// grab the next 8 bytes and try again.
165165
}
166-
result := sfreeindex + uintptr(bitIndex)
166+
result := sfreeindex + uint16(bitIndex)
167167
if result >= snelems {
168168
s.freeindex = snelems
169169
return snelems
@@ -191,7 +191,7 @@ func (s *mspan) nextFreeIndex() uintptr {
191191
// been no preemption points since ensuring this (which could allow a
192192
// GC transition, which would allow the state to change).
193193
func (s *mspan) isFree(index uintptr) bool {
194-
if index < s.freeIndexForScan {
194+
if index < uintptr(s.freeIndexForScan) {
195195
return false
196196
}
197197
bytep, mask := s.allocBits.bitp(index)
@@ -751,7 +751,7 @@ func (s *mspan) initHeapBits(forceClear bool) {
751751
// scanning the allocation bitmap.
752752
func (s *mspan) countAlloc() int {
753753
count := 0
754-
bytes := divRoundUp(s.nelems, 8)
754+
bytes := divRoundUp(uintptr(s.nelems), 8)
755755
// Iterate over each 8-byte chunk and count allocations
756756
// with an intrinsic. Note that newMarkBits guarantees that
757757
// gcmarkBits will be 8-byte aligned, so we don't have to

src/runtime/mcache.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func (c *mcache) refill(spc spanClass) {
148148
// Return the current cached span to the central lists.
149149
s := c.alloc[spc]
150150

151-
if uintptr(s.allocCount) != s.nelems {
151+
if s.allocCount != s.nelems {
152152
throw("refill of span with free space remaining")
153153
}
154154
if s != &emptymspan {
@@ -184,7 +184,7 @@ func (c *mcache) refill(spc spanClass) {
184184
throw("out of memory")
185185
}
186186

187-
if uintptr(s.allocCount) == s.nelems {
187+
if s.allocCount == s.nelems {
188188
throw("span has no free space")
189189
}
190190

@@ -284,7 +284,7 @@ func (c *mcache) releaseAll() {
284284
//
285285
// If this span was cached before sweep, then gcController.heapLive was totally
286286
// recomputed since caching this span, so we don't do this for stale spans.
287-
dHeapLive -= int64(s.nelems-uintptr(s.allocCount)) * int64(s.elemsize)
287+
dHeapLive -= int64(s.nelems-s.allocCount) * int64(s.elemsize)
288288
}
289289

290290
// Release the span to the mcentral.

src/runtime/mcentral.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ havespan:
174174
traceGCSweepDone()
175175
}
176176
n := int(s.nelems) - int(s.allocCount)
177-
if n == 0 || s.freeindex == s.nelems || uintptr(s.allocCount) == s.nelems {
177+
if n == 0 || s.freeindex == s.nelems || s.allocCount == s.nelems {
178178
throw("span has no free objects")
179179
}
180180
freeByteBase := s.freeindex &^ (64 - 1)

src/runtime/mgcsweep.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -602,8 +602,8 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
602602
// efficient; allocfreetrace has massive overhead.
603603
mbits := s.markBitsForBase()
604604
abits := s.allocBitsForIndex(0)
605-
for i := uintptr(0); i < s.nelems; i++ {
606-
if !mbits.isMarked() && (abits.index < s.freeindex || abits.isMarked()) {
605+
for i := uintptr(0); i < uintptr(s.nelems); i++ {
606+
if !mbits.isMarked() && (abits.index < uintptr(s.freeindex) || abits.isMarked()) {
607607
x := s.base() + i*s.elemsize
608608
if debug.allocfreetrace != 0 {
609609
tracefree(unsafe.Pointer(x), size)
@@ -634,12 +634,12 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
634634
//
635635
// Check the first bitmap byte, where we have to be
636636
// careful with freeindex.
637-
obj := s.freeindex
637+
obj := uintptr(s.freeindex)
638638
if (*s.gcmarkBits.bytep(obj / 8)&^*s.allocBits.bytep(obj / 8))>>(obj%8) != 0 {
639639
s.reportZombies()
640640
}
641641
// Check remaining bytes.
642-
for i := obj/8 + 1; i < divRoundUp(s.nelems, 8); i++ {
642+
for i := obj/8 + 1; i < divRoundUp(uintptr(s.nelems), 8); i++ {
643643
if *s.gcmarkBits.bytep(i)&^*s.allocBits.bytep(i) != 0 {
644644
s.reportZombies()
645645
}
@@ -666,7 +666,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
666666
// gcmarkBits becomes the allocBits.
667667
// get a fresh cleared gcmarkBits in preparation for next GC
668668
s.allocBits = s.gcmarkBits
669-
s.gcmarkBits = newMarkBits(s.nelems)
669+
s.gcmarkBits = newMarkBits(uintptr(s.nelems))
670670

671671
// refresh pinnerBits if they exists
672672
if s.pinnerBits != nil {
@@ -760,7 +760,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
760760
return true
761761
}
762762
// Return span back to the right mcentral list.
763-
if uintptr(nalloc) == s.nelems {
763+
if nalloc == s.nelems {
764764
mheap_.central[spc].mcentral.fullSwept(sweepgen).push(s)
765765
} else {
766766
mheap_.central[spc].mcentral.partialSwept(sweepgen).push(s)
@@ -829,10 +829,10 @@ func (s *mspan) reportZombies() {
829829
print("runtime: marked free object in span ", s, ", elemsize=", s.elemsize, " freeindex=", s.freeindex, " (bad use of unsafe.Pointer? try -d=checkptr)\n")
830830
mbits := s.markBitsForBase()
831831
abits := s.allocBitsForIndex(0)
832-
for i := uintptr(0); i < s.nelems; i++ {
832+
for i := uintptr(0); i < uintptr(s.nelems); i++ {
833833
addr := s.base() + i*s.elemsize
834834
print(hex(addr))
835-
alloc := i < s.freeindex || abits.isMarked()
835+
alloc := i < uintptr(s.freeindex) || abits.isMarked()
836836
if alloc {
837837
print(" alloc")
838838
} else {

src/runtime/mheap.go

+12-13
Original file line numberDiff line numberDiff line change
@@ -435,10 +435,17 @@ type mspan struct {
435435
// undefined and should never be referenced.
436436
//
437437
// Object n starts at address n*elemsize + (start << pageShift).
438-
freeindex uintptr
438+
freeindex uint16
439439
// TODO: Look up nelems from sizeclass and remove this field if it
440440
// helps performance.
441-
nelems uintptr // number of object in the span.
441+
nelems uint16 // number of object in the span.
442+
// freeIndexForScan is like freeindex, except that freeindex is
443+
// used by the allocator whereas freeIndexForScan is used by the
444+
// GC scanner. They are two fields so that the GC sees the object
445+
// is allocated only when the object and the heap bits are
446+
// initialized (see also the assignment of freeIndexForScan in
447+
// mallocgc, and issue 54596).
448+
freeIndexForScan uint16
442449

443450
// Cache of the allocBits at freeindex. allocCache is shifted
444451
// such that the lowest bit corresponds to the bit freeindex.
@@ -495,14 +502,6 @@ type mspan struct {
495502
speciallock mutex // guards specials list and changes to pinnerBits
496503
specials *special // linked list of special records sorted by offset.
497504
userArenaChunkFree addrRange // interval for managing chunk allocation
498-
499-
// freeIndexForScan is like freeindex, except that freeindex is
500-
// used by the allocator whereas freeIndexForScan is used by the
501-
// GC scanner. They are two fields so that the GC sees the object
502-
// is allocated only when the object and the heap bits are
503-
// initialized (see also the assignment of freeIndexForScan in
504-
// mallocgc, and issue 54596).
505-
freeIndexForScan uintptr
506505
}
507506

508507
func (s *mspan) base() uintptr {
@@ -1403,16 +1402,16 @@ func (h *mheap) initSpan(s *mspan, typ spanAllocType, spanclass spanClass, base,
14031402
s.divMul = 0
14041403
} else {
14051404
s.elemsize = uintptr(class_to_size[sizeclass])
1406-
s.nelems = nbytes / s.elemsize
1405+
s.nelems = uint16(nbytes / s.elemsize)
14071406
s.divMul = class_to_divmagic[sizeclass]
14081407
}
14091408

14101409
// Initialize mark and allocation structures.
14111410
s.freeindex = 0
14121411
s.freeIndexForScan = 0
14131412
s.allocCache = ^uint64(0) // all 1s indicating all free.
1414-
s.gcmarkBits = newMarkBits(s.nelems)
1415-
s.allocBits = newAllocBits(s.nelems)
1413+
s.gcmarkBits = newMarkBits(uintptr(s.nelems))
1414+
s.allocBits = newAllocBits(uintptr(s.nelems))
14161415

14171416
// It's safe to access h.sweepgen without the heap lock because it's
14181417
// only ever updated with the world stopped and we run on the

src/runtime/pinner.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,14 @@ func (p *pinnerBits) ofObject(n uintptr) pinState {
267267
}
268268

269269
func (s *mspan) pinnerBitSize() uintptr {
270-
return divRoundUp(s.nelems*2, 8)
270+
return divRoundUp(uintptr(s.nelems)*2, 8)
271271
}
272272

273273
// newPinnerBits returns a pointer to 8 byte aligned bytes to be used for this
274274
// span's pinner bits. newPinneBits is used to mark objects that are pinned.
275275
// They are copied when the span is swept.
276276
func (s *mspan) newPinnerBits() *pinnerBits {
277-
return (*pinnerBits)(newMarkBits(s.nelems * 2))
277+
return (*pinnerBits)(newMarkBits(uintptr(s.nelems) * 2))
278278
}
279279

280280
// nosplit, because it's called by isPinned, which is nosplit

0 commit comments

Comments
 (0)