Browse Source

Remove HeapInc.FirstFlag.

Rika Ichinose 2 months ago
parent
commit
0a45266aec
1 changed files with 30 additions and 39 deletions
  1. 30 39
      rtl/inc/heap.inc

+ 30 - 39
rtl/inc/heap.inc

@@ -279,14 +279,13 @@ type
   const
   const
     SizeIndexBits = 1 + trunc(ln(FixedSizesCount - 1) /  ln(2));
     SizeIndexBits = 1 + trunc(ln(FixedSizesCount - 1) /  ln(2));
     SizeIndexMask = 1 shl SizeIndexBits - 1;
     SizeIndexMask = 1 shl SizeIndexBits - 1;
-    FixedBitPos = {$if SizeIndexBits >= 4} SizeIndexBits {$else} 4 {$endif}; { Variable chunks use 4 low bits for used / first / last / fixed arena. }
+    FixedBitPos = {$if SizeIndexBits >= 3} SizeIndexBits {$else} 3 {$endif}; { Variable chunks use 3 low bits for used / last / fixed arena. }
     FixedFlag = 1 shl FixedBitPos;
     FixedFlag = 1 shl FixedBitPos;
     FixedArenaOffsetShift = {$if FixedBitPos + 1 >= 5} FixedBitPos + 1 {$else} 5 {$endif}; { VarSizeQuant must be at least 2^5 to fit 3 64-bit pointers (RbTree.Node). }
     FixedArenaOffsetShift = {$if FixedBitPos + 1 >= 5} FixedBitPos + 1 {$else} 5 {$endif}; { VarSizeQuant must be at least 2^5 to fit 3 64-bit pointers (RbTree.Node). }
 
 
     UsedFlag = 1 shl 0;
     UsedFlag = 1 shl 0;
-    FirstFlag = 1 shl 1;
-    LastFlag = 1 shl 2;
-    FixedArenaFlag = 1 shl 3;
+    LastFlag = 1 shl 1;
+    FixedArenaFlag = 1 shl 2;
     VarSizeQuant = 1 shl FixedArenaOffsetShift; {$if VarSizeQuant < Alignment} {$error Assumed to be >= Alignment.} {$endif} {$if VarSizeQuant < 3 * sizeof(pointer)} {$error Must fit RbTree.Node.} {$endif}
     VarSizeQuant = 1 shl FixedArenaOffsetShift; {$if VarSizeQuant < Alignment} {$error Assumed to be >= Alignment.} {$endif} {$if VarSizeQuant < 3 * sizeof(pointer)} {$error Must fit RbTree.Node.} {$endif}
     VarSizeMask = SizeUint(-VarSizeQuant);
     VarSizeMask = SizeUint(-VarSizeQuant);
     HugeHeader = 0; { Special header value for huge chunks. FixedFlag must be 0, and the value must be impossible for a variable chunk. 0 turns out to be suitable. :) }
     HugeHeader = 0; { Special header value for huge chunks. FixedFlag must be 0, and the value must be impossible for a variable chunk. 0 turns out to be suitable. :) }
@@ -301,9 +300,9 @@ type
 
 
       Variable chunk header, assuming SizeIndexBits = 4:
       Variable chunk header, assuming SizeIndexBits = 4:
       h[0] = used flag (h and UsedFlag <> 0)
       h[0] = used flag (h and UsedFlag <> 0)
-      h[1] = first flag (h and FirstFlag <> 0)
-      h[2] = last flag (h and LastFlag <> 0)
-      h[3] = fixed arena flag (h and FixedArenaFlag <> 0)
+      h[1] = last flag (h and LastFlag <> 0)
+      h[2] = fixed arena flag (h and FixedArenaFlag <> 0)
+      h[3] = unused
       h[4] = 0 (h and FixedFlag = 0)
       h[4] = 0 (h and FixedFlag = 0)
       h[5:31] = size, rounded up to 32 (VarSizeQuant), shr 5; in other words, size = h and VarSizeMask.
       h[5:31] = size, rounded up to 32 (VarSizeQuant), shr 5; in other words, size = h and VarSizeMask.
 
 
@@ -370,7 +369,6 @@ type
 
 
       firstFreeChunk: pFreeChunk;
       firstFreeChunk: pFreeChunk;
       usedSize, formattedSize, fullThreshold: uint32;
       usedSize, formattedSize, fullThreshold: uint32;
-      sizeIndex: uint32; { For what size it was used the last time. Allows for a small optimization when reusing fixed arenas. }
       prev, next: pFixedArena;
       prev, next: pFixedArena;
     end;
     end;
 
 
@@ -430,7 +428,7 @@ type
       function FreeFixed(p: pointer): SizeUint;
       function FreeFixed(p: pointer): SizeUint;
 
 
       function GetOSChunk(minSize, maxSize: SizeUint): pOSChunk;
       function GetOSChunk(minSize, maxSize: SizeUint): pOSChunk;
-      function AllocateOSChunk(minSize: SizeUint): pOSChunk;
+      function AllocateOSChunk(minSize, maxSize: SizeUint): pOSChunk;
 
 
       function AllocVar(size: SizeUint; isArena: boolean): pointer;
       function AllocVar(size: SizeUint; isArena: boolean): pointer;
       function FreeVar(p: pointer): SizeUint;
       function FreeVar(p: pointer): SizeUint;
@@ -949,8 +947,6 @@ type
           write(f, ', used')
           write(f, ', used')
         else
         else
           write(f, ', f r e e');
           write(f, ', f r e e');
-        if pVarHeader(p - VarHeaderSize)^.ch.h and FirstFlag <> 0 then
-          write(f, ', first');
         if pVarHeader(p - VarHeaderSize)^.ch.h and LastFlag <> 0 then
         if pVarHeader(p - VarHeaderSize)^.ch.h and LastFlag <> 0 then
           write(f, ', last');
           write(f, ', last');
         if pVarHeader(p - VarHeaderSize)^.ch.h and FixedArenaFlag <> 0 then
         if pVarHeader(p - VarHeaderSize)^.ch.h and FixedArenaFlag <> 0 then
@@ -1010,7 +1006,7 @@ type
       result := MinFixedArenaSize;
       result := MinFixedArenaSize;
     if result > MaxFixedArenaSize then
     if result > MaxFixedArenaSize then
       result := MaxFixedArenaSize;
       result := MaxFixedArenaSize;
-    dec(result, VarHeaderSize + FixedArenaDataOffset); { Prettier fit into OS chunks. }
+    dec(result, VarHeaderSize + VarSizeQuant); { Prettier fit into OS chunks. }
   end;
   end;
 
 
   function HeapInc.ThreadState.AllocFixed(size: SizeUint): pointer;
   function HeapInc.ThreadState.AllocFixed(size: SizeUint): pointer;
@@ -1042,13 +1038,13 @@ type
           arena := AllocVar(ChooseFixedArenaSize(sizeIndex), true);
           arena := AllocVar(ChooseFixedArenaSize(sizeIndex), true);
           if not Assigned(arena) then
           if not Assigned(arena) then
             exit(nil);
             exit(nil);
-          arena^.sizeIndex := uint32(-1);
+          { Size index of the first chunk in the arena is used to determine if it can be reused. Set a purposely mismatching value for freshly allocated arena. }
+          pCommonHeader(pointer(arena) + FixedArenaDataOffset)^.h := uint32(not sizeIndex);
         end;
         end;
-        if arena^.sizeIndex = sizeIndex then
+        if pCommonHeader(pointer(arena) + FixedArenaDataOffset)^.h and SizeIndexMask = sizeIndex then
           { Lucky! Just don’t reset the chunk and use its old freelist. }
           { Lucky! Just don’t reset the chunk and use its old freelist. }
         else
         else
         begin
         begin
-          arena^.sizeIndex := sizeIndex;
           arena^.firstFreeChunk := nil;
           arena^.firstFreeChunk := nil;
           arena^.usedSize := 0;
           arena^.usedSize := 0;
           arena^.formattedSize := 0;
           arena^.formattedSize := 0;
@@ -1086,7 +1082,7 @@ type
     inc(arena^.usedSize, size);
     inc(arena^.usedSize, size);
     if arena^.usedSize >= arena^.fullThreshold then
     if arena^.usedSize >= arena^.fullThreshold then
     begin
     begin
-      inc(allocatedByFullArenas[sizeIndex], pVarHeader(arena)[-1].ch.h and VarSizeMask);
+      inc(allocatedByFullArenas[sizeIndex], pVarHeader(arena)[-1].ch.h); { Without masking with VarSizeMask, ch.h has parasite bits, but they don’t matter as long as they are unchanged, so the same value will be subtracted. }
       { Remove arena from partialArenas[sizeIndex]. (It was first.) }
       { Remove arena from partialArenas[sizeIndex]. (It was first.) }
       nextArena := arena^.next;
       nextArena := arena^.next;
       partialArenas[sizeIndex] := nextArena;
       partialArenas[sizeIndex] := nextArena;
@@ -1129,7 +1125,7 @@ type
     usedSize := arena^.usedSize;
     usedSize := arena^.usedSize;
     if usedSize >= arena^.fullThreshold then
     if usedSize >= arena^.fullThreshold then
     begin
     begin
-      dec(allocatedByFullArenas[sizeIndex], pVarHeader(arena)[-1].ch.h and VarSizeMask);
+      dec(allocatedByFullArenas[sizeIndex], pVarHeader(arena)[-1].ch.h);
       { Add arena to partialArenas[sizeIndex]. }
       { Add arena to partialArenas[sizeIndex]. }
       nextArena := partialArenas[sizeIndex];
       nextArena := partialArenas[sizeIndex];
       arena^.next := nextArena;
       arena^.next := nextArena;
@@ -1188,17 +1184,17 @@ type
       end;
       end;
     end;
     end;
   {$endif FPC_HAS_FEATURE_THREADING and not HAS_SYSOSFREE}
   {$endif FPC_HAS_FEATURE_THREADING and not HAS_SYSOSFREE}
-    result := AllocateOSChunk(minSize);
+    result := AllocateOSChunk(minSize, maxSize);
   end;
   end;
 
 
-  function HeapInc.ThreadState.AllocateOSChunk(minSize: SizeUint): pOSChunk;
+  function HeapInc.ThreadState.AllocateOSChunk(minSize, maxSize: SizeUint): pOSChunk;
   var
   var
     query, statv: SizeUint;
     query, statv: SizeUint;
   begin
   begin
     query := used div 16 + minSize div 2; { Base: 6.25% of the memory used, so if GrowHeapSize2 = 1 Mb, 1 Mb OS allocations start at 16 Mb used. }
     query := used div 16 + minSize div 2; { Base: 6.25% of the memory used, so if GrowHeapSize2 = 1 Mb, 1 Mb OS allocations start at 16 Mb used. }
-    if query > GrowHeapSize2 then { Limit by GrowHeapSize2. }
-      query := GrowHeapSize2;
-    if query < minSize then { But of course allocate at least the amount requested. }
+    if query > maxSize then { Limit by maxSize (usually GrowHeapSize2). }
+      query := maxSize;
+    if query < minSize then { But of course allocate at least the amount requested. Also triggers if maxSize was wrong (smaller than minSize). }
       query := minSize;
       query := minSize;
     query := (query + (OSChunkVarSizeQuant - 1)) and SizeUint(-OSChunkVarSizeQuant); { Quantize. }
     query := (query + (OSChunkVarSizeQuant - 1)) and SizeUint(-OSChunkVarSizeQuant); { Quantize. }
     result := SysOSAlloc(query);
     result := SysOSAlloc(query);
@@ -1221,7 +1217,7 @@ type
   var
   var
     fv, toRemove: pFreeVarChunk;
     fv, toRemove: pFreeVarChunk;
     osChunk, osNext: pVarOSChunk;
     osChunk, osNext: pVarOSChunk;
-    vSize, minSize, maxSize, statv: SizeUint;
+    vSize, statv: SizeUint;
     adj: RbTree.pNode;
     adj: RbTree.pNode;
   begin
   begin
     size := (size + (VarHeaderSize + VarSizeQuant - 1)) and SizeUint(-VarSizeQuant);
     size := (size + (VarHeaderSize + VarSizeQuant - 1)) and SizeUint(-VarSizeQuant);
@@ -1240,11 +1236,7 @@ type
     end else
     end else
     begin
     begin
       toRemove := nil;
       toRemove := nil;
-      minSize := VarOSChunkDataOffset + size;
-      maxSize := GrowHeapSize2;
-      if maxSize < minSize then
-        maxSize := High(SizeUint); { Shouldn’t happen (unless GrowHeapSize2 is too small), so don’t optimize the case when it happens. }
-      osChunk := pVarOSChunk(GetOSChunk(minSize, maxSize));
+      osChunk := pVarOSChunk(GetOSChunk(VarOSChunkDataOffset + size, GrowHeapSize2));
       if not Assigned(osChunk) then
       if not Assigned(osChunk) then
         exit(nil);
         exit(nil);
 
 
@@ -1263,7 +1255,7 @@ type
       pVarHeader(pointer(fv) - VarHeaderSize)^.threadState := @self;
       pVarHeader(pointer(fv) - VarHeaderSize)^.threadState := @self;
     {$endif}
     {$endif}
       vSize := SizeUint(osChunk^.size - VarOSChunkDataOffset) and SizeUint(-VarSizeQuant);
       vSize := SizeUint(osChunk^.size - VarOSChunkDataOffset) and SizeUint(-VarSizeQuant);
-      pVarHeader(pointer(fv) - VarHeaderSize)^.ch.h := uint32(vSize) + (FirstFlag or LastFlag);
+      pVarHeader(pointer(fv) - VarHeaderSize)^.ch.h := uint32(vSize) + LastFlag;
       fv^.size := vSize;
       fv^.size := vSize;
     end;
     end;
 
 
@@ -1300,15 +1292,14 @@ type
       if not Assigned(toRemove) then
       if not Assigned(toRemove) then
         varFree.Add(@fv^.rbn);
         varFree.Add(@fv^.rbn);
 
 
-      { Allocated chunk is still first in the OS chunk if the original chunk was first. }
-      pVarHeader(result - VarHeaderSize)^.ch.h := pVarHeader(result - VarHeaderSize)^.ch.h and FirstFlag + UsedFlag + uint32(size);
+      pVarHeader(result - VarHeaderSize)^.ch.h := uint32(size) + UsedFlag;
     end else
     end else
     begin
     begin
       { Use the entire chunk. }
       { Use the entire chunk. }
       if Assigned(toRemove) then
       if Assigned(toRemove) then
         varFree.Remove(@toRemove^.rbn);
         varFree.Remove(@toRemove^.rbn);
       size := fv^.size;
       size := fv^.size;
-      pVarHeader(result - VarHeaderSize)^.ch.h := pVarHeader(result - VarHeaderSize)^.ch.h and (FirstFlag or LastFlag) + UsedFlag + uint32(size);
+      pVarHeader(result - VarHeaderSize)^.ch.h := pVarHeader(result - VarHeaderSize)^.ch.h and LastFlag + UsedFlag + uint32(size);
     end;
     end;
     if isArena then
     if isArena then
       inc(pVarHeader(result)[-1].ch.h, FixedArenaFlag) { Arenas aren’t counted in “used” directly. }
       inc(pVarHeader(result)[-1].ch.h, FixedArenaFlag) { Arenas aren’t counted in “used” directly. }
@@ -1369,14 +1360,14 @@ type
       end;
       end;
     end;
     end;
 
 
-    if fSizeFlags and FirstFlag = 0 then
+    if pVarHeader(p - VarHeaderSize)^.prevSize <> 0 then
     begin
     begin
       p2 := p - pVarHeader(p - VarHeaderSize)^.prevSize;
       p2 := p - pVarHeader(p - VarHeaderSize)^.prevSize;
       if pVarHeader(p2 - VarHeaderSize)^.ch.h and UsedFlag = 0 then
       if pVarHeader(p2 - VarHeaderSize)^.ch.h and UsedFlag = 0 then
       begin
       begin
         p := p2;
         p := p2;
-        fSizeFlags := (fSizeFlags + pFreeVarChunk(p2)^.size) + pVarHeader(p2 - VarHeaderSize)^.ch.h and FirstFlag;
-        { Keep the largest chunk in toRemove as it will have more chances for ChangeInPlace succeeding. }
+        fSizeFlags := fSizeFlags + pFreeVarChunk(p2)^.size;
+        { Keep the largest chunk in toRemove as only the largest of the two has any chances for ChangeInPlace succeeding (final free chunk is sum of these + p). }
         if Assigned(toRemove) then
         if Assigned(toRemove) then
           if pFreeVarChunk(p2)^.size >= toRemove^.size then
           if pFreeVarChunk(p2)^.size >= toRemove^.size then
           begin
           begin
@@ -1391,7 +1382,7 @@ type
 
 
     { Turn p into a free chunk and add it back to varFree...
     { Turn p into a free chunk and add it back to varFree...
       unless it spans the entire OS chunk, in which case instead move the chunk from varOS to freeOS. }
       unless it spans the entire OS chunk, in which case instead move the chunk from varOS to freeOS. }
-    if fSizeFlags and (FirstFlag or LastFlag) <> FirstFlag or LastFlag then
+    if (pVarHeader(p - VarHeaderSize)^.prevSize <> 0) or (fSizeFlags and LastFlag = 0) then
     begin
     begin
       if Assigned(toRemove) then
       if Assigned(toRemove) then
       begin
       begin
@@ -1514,7 +1505,7 @@ type
       end;
       end;
 
 
       { Update p size. }
       { Update p size. }
-      pVarHeader(result - VarHeaderSize)^.ch.h := uint32(size) + pVarHeader(result - VarHeaderSize)^.ch.h and (FirstFlag or UsedFlag);
+      pVarHeader(result - VarHeaderSize)^.ch.h := uint32(size) + UsedFlag;
     end
     end
     { Grow if there is free space. }
     { Grow if there is free space. }
     else if (pVarHeader(result - VarHeaderSize)^.ch.h and LastFlag = 0) and (pVarHeader(p2 - VarHeaderSize)^.ch.h and UsedFlag = 0) and
     else if (pVarHeader(result - VarHeaderSize)^.ch.h and LastFlag = 0) and (pVarHeader(p2 - VarHeaderSize)^.ch.h and UsedFlag = 0) and
@@ -1541,7 +1532,7 @@ type
 
 
       toRemove := p2;
       toRemove := p2;
       { Update p size. }
       { Update p size. }
-      pVarHeader(result - VarHeaderSize)^.ch.h := uint32(size) + pVarHeader(result - VarHeaderSize)^.ch.h and (FirstFlag or UsedFlag);
+      pVarHeader(result - VarHeaderSize)^.ch.h := uint32(size) + UsedFlag;
       { No empty chunk? }
       { No empty chunk? }
       if fSizeFlags = 0 then
       if fSizeFlags = 0 then
       begin
       begin
@@ -1804,7 +1795,7 @@ type
         nextArena^.prev := arena;
         nextArena^.prev := arena;
       partialArenas[sizeIndex] := arena;
       partialArenas[sizeIndex] := arena;
     end else
     end else
-      inc(allocatedByFullArenas[sizeIndex], pVarHeader(arena)[-1].ch.h and VarSizeMask);
+      inc(allocatedByFullArenas[sizeIndex], pVarHeader(arena)[-1].ch.h);
   end;
   end;
 
 
   procedure HeapInc.ThreadState.AdoptVarOwner(p: pointer);
   procedure HeapInc.ThreadState.AdoptVarOwner(p: pointer);