@@ -71,19 +71,20 @@ var zeroSizedAlloc uint8
7171type blockState uint8
7272
7373const (
74- blockStateFree blockState = 0 // 00
75- blockStateHead blockState = 1 // 01
76- blockStateTail blockState = 2 // 10
77- blockStateMark blockState = 3 // 11
78- blockStateMask blockState = 3 // 11
74+ blockStateLow blockState = 1
75+ blockStateHigh blockState = 1 << blocksPerStateByte
76+
77+ blockStateFree blockState = 0
78+ blockStateHead blockState = blockStateLow
79+ blockStateTail blockState = blockStateHigh
80+ blockStateMark blockState = blockStateLow | blockStateHigh
81+ blockStateMask blockState = blockStateLow | blockStateHigh
7982)
8083
84+ const blockStateEach = 1 << blocksPerStateByte - 1
85+
8186// The byte value of a block where every block is a 'tail' block.
82- const blockStateByteAllTails = 0 |
83- uint8 (blockStateTail << (stateBits * 3 )) |
84- uint8 (blockStateTail << (stateBits * 2 )) |
85- uint8 (blockStateTail << (stateBits * 1 )) |
86- uint8 (blockStateTail << (stateBits * 0 ))
87+ const blockStateByteAllTails = byte (blockStateTail ) * blockStateEach
8788
8889// String returns a human-readable version of the block state, for debugging.
8990func (s blockState ) String () string {
@@ -180,7 +181,7 @@ func (b gcBlock) stateByte() byte {
180181// Return the block state given a state byte. The state byte must have been
181182// obtained using b.stateByte(), otherwise the result is incorrect.
182183func (b gcBlock ) stateFromByte (stateByte byte ) blockState {
183- return blockState (stateByte >> (( b % blocksPerStateByte ) * stateBits )) & blockStateMask
184+ return blockState (stateByte >> (b % blocksPerStateByte )) & blockStateMask
184185}
185186
186187// State returns the current block state.
@@ -193,38 +194,12 @@ func (b gcBlock) state() blockState {
193194// from head to mark.
194195func (b gcBlock ) setState (newState blockState ) {
195196 stateBytePtr := (* uint8 )(unsafe .Add (metadataStart , b / blocksPerStateByte ))
196- * stateBytePtr |= uint8 (newState << (( b % blocksPerStateByte ) * stateBits ))
197+ * stateBytePtr |= uint8 (newState << (b % blocksPerStateByte ))
197198 if gcAsserts && b .state () != newState {
198199 runtimePanic ("gc: setState() was not successful" )
199200 }
200201}
201202
202- // markFree sets the block state to free, no matter what state it was in before.
203- func (b gcBlock ) markFree () {
204- stateBytePtr := (* uint8 )(unsafe .Add (metadataStart , b / blocksPerStateByte ))
205- * stateBytePtr &^= uint8 (blockStateMask << ((b % blocksPerStateByte ) * stateBits ))
206- if gcAsserts && b .state () != blockStateFree {
207- runtimePanic ("gc: markFree() was not successful" )
208- }
209- if gcAsserts {
210- * (* [wordsPerBlock ]uintptr )(unsafe .Pointer (b .address ())) = [wordsPerBlock ]uintptr {}
211- }
212- }
213-
214- // unmark changes the state of the block from mark to head. It must be marked
215- // before calling this function.
216- func (b gcBlock ) unmark () {
217- if gcAsserts && b .state () != blockStateMark {
218- runtimePanic ("gc: unmark() on a block that is not marked" )
219- }
220- clearMask := blockStateMask ^ blockStateHead // the bits to clear from the state
221- stateBytePtr := (* uint8 )(unsafe .Add (metadataStart , b / blocksPerStateByte ))
222- * stateBytePtr &^= uint8 (clearMask << ((b % blocksPerStateByte ) * stateBits ))
223- if gcAsserts && b .state () != blockStateHead {
224- runtimePanic ("gc: unmark() was not successful" )
225- }
226- }
227-
228203// objHeader is a structure prepended to every heap object to hold metadata.
229204type objHeader struct {
230205 // next is the next object to scan after this.
@@ -702,36 +677,69 @@ func markRoot(addr, root uintptr) {
702677// Sweep goes through all memory and frees unmarked memory.
703678// It returns how many bytes are free in the heap after the sweep.
704679func sweep () (freeBytes uintptr ) {
705- freeCurrentObject := false
706- var freed uint64
707- for block := gcBlock (0 ); block < endBlock ; block ++ {
708- switch block .state () {
709- case blockStateHead :
710- // Unmarked head. Free it, including all tail blocks following it.
711- block .markFree ()
712- freeCurrentObject = true
713- gcFrees ++
714- freed ++
715- case blockStateTail :
716- if freeCurrentObject {
717- // This is a tail object following an unmarked head.
718- // Free it now.
719- block .markFree ()
720- freed ++
721- }
722- case blockStateMark :
723- // This is a marked object. The next tail blocks must not be freed,
724- // but the mark bit must be removed so the next GC cycle will
725- // collect this object if it is unreferenced then.
726- block .unmark ()
727- freeCurrentObject = false
728- case blockStateFree :
729- freeBytes += bytesPerBlock
730- }
731- }
732- gcFreedBlocks += freed
733- freeBytes += uintptr (freed ) * bytesPerBlock
734- return
680+ endBlock := endBlock
681+ metadataEnd := unsafe .Add (metadataStart , (endBlock + (blocksPerStateByte - 1 ))/ blocksPerStateByte )
682+ var oldFreeBlocks , freedHeads , freedTails uintptr
683+ var carry byte
684+ // Pre-subtract the blocks that do not actually exist from oldFreeBlocks.
685+ oldFreeBlocks -= (blocksPerStateByte - 1 ) - uintptr (endBlock + (blocksPerStateByte - 1 ))% blocksPerStateByte
686+ for meta := metadataStart ; meta != metadataEnd ; meta = unsafe .Add (meta , 1 ) {
687+ // Fetch the state byte.
688+ stateBytePtr := (* byte )(unsafe .Pointer (meta ))
689+ stateByte := * stateBytePtr
690+
691+ // Count existing free blocks in the state byte.
692+ lowState := stateByte & blockStateEach
693+ highState := stateByte >> blocksPerStateByte
694+ freeBlocks := lowState | highState
695+ oldFreeBlocks += uintptr (count4LUT [freeBlocks ])
696+
697+ // Count unmarked heads in the state byte.
698+ unmarkedHeads := lowState &^ highState
699+ freedHeads += uintptr (count4LUT [unmarkedHeads ])
700+
701+ // Identify and seperate live and free tails.
702+ // Adding 1 to a run of bits will clear the run.
703+ // We can use this to clear tails after a freed head.
704+ tails := highState &^ lowState
705+ tailClear := tails + (unmarkedHeads << 1 ) + carry
706+ carry = tailClear >> blocksPerStateByte
707+ freedTails += uintptr (count4LUT [tails &^tailClear ])
708+ tails &= tailClear
709+
710+ // Construct the new state byte.
711+ markedHeads := highState & lowState
712+ * stateBytePtr = markedHeads | (tails << blocksPerStateByte )
713+ }
714+
715+ // Update the GC metrics.
716+ gcFrees += uint64 (freedHeads )
717+ freedBlocks := freedHeads + freedTails
718+ gcFreedBlocks += uint64 (freedBlocks )
719+ freeBlocks := oldFreeBlocks + freedBlocks
720+
721+ return freeBlocks * bytesPerBlock
722+ }
723+
724+ // count4LUT is a lookup table used to count set bits in a 4-bit mask.
725+ // TODO: replace with popcnt when available
726+ var count4LUT = [16 ]uint8 {
727+ 0b0000 : 0 ,
728+ 0b0001 : 1 ,
729+ 0b0010 : 1 ,
730+ 0b0011 : 2 ,
731+ 0b0100 : 1 ,
732+ 0b0101 : 2 ,
733+ 0b0110 : 2 ,
734+ 0b0111 : 3 ,
735+ 0b1000 : 1 ,
736+ 0b1001 : 2 ,
737+ 0b1010 : 2 ,
738+ 0b1011 : 3 ,
739+ 0b1100 : 2 ,
740+ 0b1101 : 3 ,
741+ 0b1110 : 3 ,
742+ 0b1111 : 4 ,
735743}
736744
737745// buildFreeRanges rebuilds the freeRanges list.
0 commit comments