Browse Source

* tiny optimization to heap manager, avoid repeated removing/readding to freeoslist overhead

git-svn-id: trunk@7236 -
micha 18 years ago
parent
commit
9c727d1762
1 changed files with 61 additions and 42 deletions
  1. 61 42
      rtl/inc/heap.inc

+ 61 - 42
rtl/inc/heap.inc

@@ -52,6 +52,9 @@ const
   usedflag       = 2;   { flag if the block is used or not }
   lastblockflag  = 4;   { flag if the block is the last in os chunk }
   firstblockflag = 8;   { flag if the block is the first in os chunk }
+  { os chunk flags }
+  ocrecycleflag  = 1;
+  { above flags stored in size field }
   sizemask = not(blocksize-1);
   fixedoffsetshift = 16;
   fixedsizemask = sizemask and ((1 shl fixedoffsetshift) - 1);
@@ -120,20 +123,22 @@ const
   still present in a freelists_fixed, therefore we can easily remove 
   the os chunk from the freeoslist if this size is needed again; we 
   don't need to search freeoslist in alloc_oschunk, since it won't
-  be present anymore if alloc_oschunk is reached.
+  be present anymore if alloc_oschunk is reached. Note that removing
+  from the freeoslist is not really done, only the recycleflag is
+  set, allowing to reset the flag easily. alloc_oschunk will clean up
+  the list while passing over it, that was a slow function anyway.
 }
 
 type
   poschunk = ^toschunk;
   toschunk = record
     size : ptrint;
-    next,
-    prev : poschunk;
+    next : poschunk;
     used : ptrint;
     { padding inserted automatically by alloc_oschunk }
   end;
 
-  pmemchunk_fixed  = ^tmemchunk_fixed;
+  pmemchunk_fixed = ^tmemchunk_fixed;
   tmemchunk_fixed = record
     { aligning is done automatically in alloc_oschunk }
     size  : ptrint;
@@ -141,7 +146,7 @@ type
     prev_fixed : pmemchunk_fixed;
   end;
 
-  pmemchunk_var  = ^tmemchunk_var;
+  pmemchunk_var = ^tmemchunk_var;
   tmemchunk_var = record
     prevsize : ptrint;
     size  : ptrint;
@@ -601,7 +606,7 @@ var
   chunkindex: ptrint;
 begin
   pmc := pmemchunk_fixed(pointer(poc)+fixedfirstoffset);
-  pmc_end := pmemchunk_fixed(pointer(poc)+poc^.size-chunksize);
+  pmc_end := pmemchunk_fixed(pointer(poc)+(poc^.size and sizemask)-chunksize);
   chunkindex := chunksize shr blockshift;
   repeat
     remove_from_list_fixed(chunkindex, pmc);
@@ -610,23 +615,31 @@ begin
 end;
 
 procedure append_to_oslist(poc: poschunk; chunksize: ptrint);
+var
+  pocsize: ptrint;
 begin
+  { check if already on list }
+  if (poc^.size and ocrecycleflag) <> 0 then
+    begin
+      inc(freeoslistcount);
+      poc^.size := poc^.size and not ocrecycleflag;
+      exit;
+    end;
   { decide whether to free block or add to list }
 {$ifdef HAS_SYSOSFREE}
+  pocsize := poc^.size and sizemask;
   if (freeoslistcount >= MaxKeptOSChunks) or
-     (poc^.size > growheapsize2) then
+     (pocsize > growheapsize2) then
     begin
       if chunksize <> 0 then
         remove_all_from_list_fixed(chunksize, poc);
-      dec(internal_status.currheapsize, poc^.size);
-      SysOSFree(poc, poc^.size);
+      dec(internal_status.currheapsize, pocsize);
+      SysOSFree(poc, pocsize);
     end
   else
     begin
 {$endif}
       poc^.next := freeoslist;
-      if freeoslist <> nil then
-        freeoslist^.prev := poc;
       freeoslist := poc;
       inc(freeoslistcount);
 {$ifdef HAS_SYSOSFREE}
@@ -634,26 +647,6 @@ begin
 {$endif}
 end;
 
-procedure remove_from_oslist(poc: poschunk);
-  { poc does not have to actually be on the oslist }
-begin
-  if not assigned(poc^.prev) then
-    if not assigned(poc^.next) then
-      if freeoslist = poc then
-        freeoslist := nil
-      else
-        exit
-    else
-      freeoslist := poc^.next
-  else
-    poc^.prev^.next := poc^.next;
-  if assigned(poc^.next) then
-    poc^.next^.prev := poc^.prev;
-  dec(freeoslistcount);
-  poc^.prev := nil;
-  poc^.next := nil;
-end;
-
 procedure clear_oschunk_on_freelist_fixed_flag(poc: poschunk); inline;
   { prevent thinking this os chunk is on the fixed freelists }
 begin
@@ -801,10 +794,12 @@ var
   pmc_next  : pmemchunk_fixed;
   pmcv      : pmemchunk_var;
   poc       : poschunk;
+  prev_poc  : poschunk;
   minsize,
   maxsize,
   i         : ptrint;
   chunksize : ptrint;
+  pocsize   : ptrint;
 begin
   { increase size by size needed for os block header }
   minsize := size + varfirstoffset;
@@ -816,18 +811,36 @@ begin
     maxsize := high(ptrint);
   { blocks available in freelist? }
   poc := freeoslist;
+  prev_poc := nil;
   while poc <> nil do
     begin
-      if (poc^.size >= minsize) and
-         (poc^.size <= maxsize) then
+      if (poc^.size and ocrecycleflag) <> 0 then
+      begin
+        { oops! we recycled this chunk; remove it from list }
+        poc^.size := poc^.size and not ocrecycleflag;
+        poc := poc^.next;
+        if prev_poc = nil then
+          freeoslist := poc
+        else
+          prev_poc^.next := poc;
+        continue;
+      end;
+      pocsize := poc^.size and sizemask;
+      if (pocsize >= minsize) and
+         (pocsize <= maxsize) then
         begin
-          size := poc^.size;
-          remove_from_oslist(poc);
+          size := pocsize;
+          if prev_poc = nil then
+            freeoslist := poc^.next
+          else
+            prev_poc^.next := poc^.next;
+          dec(freeoslistcount);
           pmc := pmemchunk_fixed(pointer(poc)+fixedfirstoffset);
           if pmc^.size <> 0 then
             remove_all_from_list_fixed(pmc^.size and fixedsizemask, poc);
           break;
         end;
+      prev_poc := poc;
       poc := poc^.next;
     end;
   if poc = nil then
@@ -879,7 +892,6 @@ begin
       end;
       { prevent thinking this os chunk is on some freelist }
       clear_oschunk_on_freelist_fixed_flag(poc);
-      poc^.prev := nil;
       poc^.next := nil;
       { set the total new heap size }
       inc(internal_status.currheapsize,size);
@@ -944,11 +956,22 @@ begin
   chunkindex := chunksize shr blockshift;
   pmc := freelists_fixed[chunkindex];
   { no free blocks ? }
-  if not assigned(pmc) then
+  if assigned(pmc) then
+    begin
+      { remove oschunk from free list in case we recycle it }
+      poc := poschunk(pointer(pmc) - (pmc^.size shr fixedoffsetshift));
+      if poc^.used = 0 then
+        begin
+          poc^.size := poc^.size or ocrecycleflag;
+          dec(freeoslistcount);
+        end;
+    end
+  else
     begin
       pmc := alloc_oschunk(chunkindex, chunksize);
       if not assigned(pmc) then
         exit(nil);
+      poc := poschunk(pointer(pmc)-fixedfirstoffset);
     end;
   { get a pointer to the block we should return }
   result := pointer(pmc)+sizeof(tmemchunk_fixed_hdr);
@@ -957,10 +980,6 @@ begin
   freelists_fixed[chunkindex] := pmc_next;
   if assigned(pmc_next) then
     pmc_next^.prev_fixed := nil;
-  { remove oschunk from free list in case we recycle it }
-  poc := poschunk(pointer(pmc) - (pmc^.size shr fixedoffsetshift));
-  if poc^.used = 0 then
-    remove_from_oslist(poc);
   inc(poc^.used);
   { statistics }
   inc(internal_status.currheapused,chunksize);
@@ -1367,7 +1386,7 @@ begin
   while assigned(freeoslist) do
     begin
       poc:=freeoslist^.next;
-      SysOSFree(freeoslist, freeoslist^.size);
+      SysOSFree(freeoslist, freeoslist^.size and sizemask);
       dec(freeoslistcount);
       freeoslist:=poc;
     end;