+ heap manager: reduce overhead on fixed size chunks from 8 to 4

git-svn-id: trunk@4904 -
This commit is contained in:
micha 2006-10-14 15:05:24 +00:00
parent f102f33426
commit dc3b37ff73

View File

@ -53,7 +53,8 @@ const
lastblockflag = 4; { flag if the block is the last in os chunk } lastblockflag = 4; { flag if the block is the last in os chunk }
firstblockflag = 8; { flag if the block is the first in os chunk } firstblockflag = 8; { flag if the block is the first in os chunk }
sizemask = not(blocksize-1); sizemask = not(blocksize-1);
fixedsizemask = sizemask and $ffff; fixedoffsetshift = 16;
fixedsizemask = sizemask and ((1 shl fixedoffsetshift) - 1);
{****************************************************************************} {****************************************************************************}
@ -95,32 +96,26 @@ const
{$ifndef HAS_MEMORYMANAGER} {$ifndef HAS_MEMORYMANAGER}
type type
poschunk = ^toschunk; poschunk = ^toschunk;
{ keep size of this record dividable by 16 }
toschunk = record toschunk = record
size, size : ptrint;
used,
chunkindex : ptrint;
next, next,
prev : poschunk; prev : poschunk;
{$ifdef CPU64} used : ptrint;
pad1 : array[0..0] of pointer; { padding inserted automatically by alloc_oschunk }
{$else CPU64}
pad1 : array[0..2] of pointer;
{$endif CPU64}
end; end;
pmemchunk_fixed = ^tmemchunk_fixed; pmemchunk_fixed = ^tmemchunk_fixed;
tmemchunk_fixed = record tmemchunk_fixed = record
{ aligning is done automatically in alloc_oschunk }
size : ptrint; size : ptrint;
poc : poschunk;
next_fixed, next_fixed,
prev_fixed : pmemchunk_fixed; prev_fixed : pmemchunk_fixed;
end; end;
pmemchunk_var = ^tmemchunk_var; pmemchunk_var = ^tmemchunk_var;
tmemchunk_var = record tmemchunk_var = record
size : ptrint;
prevsize : ptrint; prevsize : ptrint;
size : ptrint;
next_var, next_var,
prev_var : pmemchunk_var; prev_var : pmemchunk_var;
end; end;
@ -128,21 +123,25 @@ type
{ ``header'', ie. size of structure valid when chunk is in use } { ``header'', ie. size of structure valid when chunk is in use }
{ should correspond to tmemchunk_var_hdr structure starting with the { should correspond to tmemchunk_var_hdr structure starting with the
last field. Reason is that the overlap is starting from the end of the last field. Reason is that the overlap is starting from the end of the
record. record. }
Alignment is 8 bytes for 32bit machines. This required
for x86 MMX/SSE and for sparc Double values }
tmemchunk_fixed_hdr = record tmemchunk_fixed_hdr = record
{ aligning is done automatically in alloc_oschunk }
size : ptrint; size : ptrint;
poschunk : pointer;
end; end;
tmemchunk_var_hdr = record tmemchunk_var_hdr = record
prevsize, prevsize : ptrint;
size : ptrint; size : ptrint;
end; end;
tfreelists = array[1..maxblockindex] of pmemchunk_fixed; tfreelists = array[1..maxblockindex] of pmemchunk_fixed;
pfreelists = ^tfreelists; pfreelists = ^tfreelists;
const
fixedfirstoffset = ((sizeof(toschunk) + sizeof(tmemchunk_fixed_hdr) + $f)
and not $f) - sizeof(tmemchunk_fixed_hdr);
varfirstoffset = ((sizeof(toschunk) + sizeof(tmemchunk_var_hdr) + $f)
and not $f) - sizeof(tmemchunk_var_hdr);
var var
internal_status : TFPCHeapStatus; internal_status : TFPCHeapStatus;
@ -522,7 +521,7 @@ end;
List adding/removal List adding/removal
*****************************************************************************} *****************************************************************************}
procedure append_to_list_var(pmc: pmemchunk_var);inline; procedure append_to_list_var(pmc: pmemchunk_var); inline;
begin begin
pmc^.prev_var := nil; pmc^.prev_var := nil;
pmc^.next_var := freelist_var; pmc^.next_var := freelist_var;
@ -531,7 +530,17 @@ begin
freelist_var := pmc; freelist_var := pmc;
end; end;
procedure remove_from_list_var(pmc: pmemchunk_var);inline; procedure remove_from_list_fixed(blockindex: ptrint; pmc: pmemchunk_fixed); inline;
begin
if assigned(pmc^.next_fixed) then
pmc^.next_fixed^.prev_fixed := pmc^.prev_fixed;
if assigned(pmc^.prev_fixed) then
pmc^.prev_fixed^.next_fixed := pmc^.next_fixed
else
freelists_fixed[blockindex] := pmc^.next_fixed;
end;
procedure remove_from_list_var(pmc: pmemchunk_var); inline;
begin begin
if assigned(pmc^.next_var) then if assigned(pmc^.next_var) then
pmc^.next_var^.prev_var := pmc^.prev_var; pmc^.next_var^.prev_var := pmc^.prev_var;
@ -581,32 +590,23 @@ var
poc: poschunk; poc: poschunk;
begin begin
// block eligable for freeing // block eligable for freeing
poc := pointer(pmc)-sizeof(toschunk); poc := pointer(pmc)-varfirstoffset;
remove_from_list_var(pmc); remove_from_list_var(pmc);
append_to_oslist(poc); append_to_oslist(poc);
end; end;
procedure append_to_oslist_fixed(poc: poschunk); procedure append_to_oslist_fixed(chunkindex, chunksize: ptrint; poc: poschunk);
var var
pmc: pmemchunk_fixed; pmc: pmemchunk_fixed;
chunksize, i, size: ptrint;
chunkindex,
i, count: ptrint;
begin begin
chunkindex:=poc^.chunkindex; size := poc^.size;
chunksize:=chunkindex shl blockshift; i := fixedfirstoffset;
pmc := pmemchunk_fixed(pointer(poc)+sizeof(toschunk)); repeat
count := (poc^.size - sizeof(toschunk)) div chunksize; pmc := pmemchunk_fixed(pointer(poc)+i);
for i := 0 to count - 1 do remove_from_list_fixed(chunkindex, pmc);
begin inc(i, chunksize);
if assigned(pmc^.next_fixed) then until i > size - chunksize;
pmc^.next_fixed^.prev_fixed := pmc^.prev_fixed;
if assigned(pmc^.prev_fixed) then
pmc^.prev_fixed^.next_fixed := pmc^.next_fixed
else
freelists_fixed[chunkindex] := pmc^.next_fixed;
pmc := pointer(pmc)+chunksize;
end;
append_to_oslist(poc); append_to_oslist(poc);
end; end;
@ -734,24 +734,23 @@ end;
Grow Heap Grow Heap
*****************************************************************************} *****************************************************************************}
function alloc_oschunk(chunkindex, size: ptrint):pointer; function alloc_oschunk(chunkindex, size: ptrint): pointer;
var var
pmcfirst, pmc,
pmclast, pmc_next : pmemchunk_fixed;
pmc : pmemchunk_fixed;
pmcv : pmemchunk_var; pmcv : pmemchunk_var;
poc : poschunk; poc : poschunk;
chunksize,
minsize, minsize,
maxsize, maxsize,
i, count : ptrint; i : ptrint;
chunksize : ptrint;
begin begin
result:=nil;
chunksize:=chunkindex shl blockshift;
{ increase size by size needed for os block header } { increase size by size needed for os block header }
minsize := size + sizeof(toschunk); minsize := size + varfirstoffset;
{ for fixed size chunks we keep offset from os chunk to mem chunk in
upper bits, so maximum os chunk size is 64K on 32bit for fixed size }
if chunkindex<>0 then if chunkindex<>0 then
maxsize := (chunksize * $ffff) + sizeof(toschunk) maxsize := 1 shl (32-fixedoffsetshift)
else else
maxsize := high(ptrint); maxsize := high(ptrint);
{ blocks available in freelist? } { blocks available in freelist? }
@ -770,13 +769,13 @@ begin
if poc = nil then if poc = nil then
begin begin
{$ifdef DUMPGROW} {$ifdef DUMPGROW}
writeln('growheap(',size,') allocating ',(size+sizeof(toschunk)+$ffff) and $ffff0000); writeln('growheap(',size,') allocating ',(size+sizeof(toschunk)+$ffff) and not $ffff);
DumpBlocks; DumpBlocks;
{$endif} {$endif}
{ allocate by 64K size } { allocate by 64K size }
size := (size+sizeof(toschunk)+$ffff) and not $ffff; size := (size+varfirstoffset+$ffff) and not $ffff;
{ allocate smaller blocks for fixed-size chunks } { allocate smaller blocks for fixed-size chunks }
if chunksize<>0 then if chunkindex<>0 then
begin begin
poc := SysOSAlloc(GrowHeapSizeSmall); poc := SysOSAlloc(GrowHeapSizeSmall);
if poc<>nil then if poc<>nil then
@ -803,7 +802,7 @@ begin
if poc=nil then if poc=nil then
begin begin
poc := SysOSAlloc(size); poc := SysOSAlloc(size);
if (poc=nil) then if poc=nil then
begin begin
if ReturnNilIfGrowHeapFails then if ReturnNilIfGrowHeapFails then
exit exit
@ -819,46 +818,47 @@ begin
{ initialize os-block } { initialize os-block }
poc^.used := 0; poc^.used := 0;
poc^.size := size; poc^.size := size;
poc^.chunkindex := chunkindex;
{ initialized oschunck for fixed chunks }
if chunkindex<>0 then if chunkindex<>0 then
begin begin
{ chop os chunk in fixedsize parts, { chop os chunk in fixedsize parts,
maximum of $ffff elements are allowed, otherwise maximum of $ffff elements are allowed, otherwise
there will be an overflow } there will be an overflow }
count := (size-sizeof(toschunk)) div chunksize; chunksize := chunkindex shl blockshift;
if count>$ffff then if size-chunksize>maxsize then
HandleError(204); HandleError(204);
{ Initialize linkedlist of chunks, the first chunk { we need to align the user pointers to 8 byte at least for
is pmemchunk_fixed(poc) and the last chunk will be in pmc at mmx/sse and doubles on sparc, align to 16 bytes }
the end of the loop } i := fixedfirstoffset;
pmcfirst := pmemchunk_fixed(pointer(poc)+sizeof(toschunk)); result := pointer(poc) + i;
pmc:=pmcfirst; pmc := pmemchunk_fixed(result);
for i:=1 to count do pmc^.prev_fixed := nil;
begin repeat
pmc^.poc:=poc; pmc^.size := fixedsizeflag or chunksize or (i shl fixedoffsetshift);
pmc^.size:=chunksize or fixedsizeflag;
pmc^.prev_fixed := pointer(pmc)-chunksize;
pmc^.next_fixed := pointer(pmc)+chunksize; pmc^.next_fixed := pointer(pmc)+chunksize;
inc(i, chunksize);
if i <= size - chunksize then
begin
pmc := pmemchunk_fixed(pointer(pmc)+chunksize); pmc := pmemchunk_fixed(pointer(pmc)+chunksize);
end; pmc^.prev_fixed := pointer(pmc)-chunksize;
{ undo last increase to get last chunk } end
pmclast := pmemchunk_fixed(pointer(pmc)-chunksize); else
{ Add to freelist and fixup first and last chunk } break;
pmclast^.next_fixed := freelists_fixed[chunkindex]; until false;
if freelists_fixed[chunkindex]<>nil then pmc_next := freelists_fixed[chunkindex];
freelists_fixed[chunkindex]^.prev_fixed := pmclast; pmc^.next_fixed := pmc_next;
freelists_fixed[chunkindex] := pmcfirst; if pmc_next<>nil then
pmemchunk_fixed(poc)^.prev_fixed:=nil; pmc_next^.prev_fixed := pmc;
result:=pmcfirst; freelists_fixed[chunkindex] := pmemchunk_fixed(result);
end end
else else
begin begin
pmcv := pmemchunk_var(pointer(poc)+sizeof(toschunk)); { we need to align the user pointers to 8 byte at least for
mmx/sse and doubles on sparc, align to 16 bytes }
result := pointer(poc)+varfirstoffset;
pmcv := pmemchunk_var(result);
append_to_list_var(pmcv); append_to_list_var(pmcv);
pmcv^.size := ((size-sizeof(toschunk)) and sizemask) or (firstblockflag or lastblockflag); pmcv^.size := ((size-varfirstoffset) and sizemask) or (firstblockflag or lastblockflag);
pmcv^.prevsize := 0; pmcv^.prevsize := 0;
result:=pmcv;
end; end;
end; end;
@ -866,41 +866,40 @@ end;
SysGetMem SysGetMem
*****************************************************************************} *****************************************************************************}
function SysGetMem_Fixed(size: ptrint): pointer; function SysGetMem_Fixed(chunksize: ptrint): pointer;
var var
pmc,hp : pmemchunk_fixed; pmc, pmc_next: pmemchunk_fixed;
poc : poschunk; poc: poschunk;
chunkindex : ptrint; chunkindex: ptrint;
begin begin
{ try to find a block in one of the freelists per size } { try to find a block in one of the freelists per size }
chunkindex := size shr blockshift; chunkindex := chunksize shr blockshift;
pmc := freelists_fixed[chunkindex]; pmc := freelists_fixed[chunkindex];
result:=nil; result:=nil;
{ no free blocks ? } { no free blocks ? }
if not assigned(pmc) then if not assigned(pmc) then
begin begin
pmc:=alloc_oschunk(chunkindex, size); pmc := alloc_oschunk(chunkindex, chunksize);
if not assigned(pmc) then if not assigned(pmc) then
exit; exit;
end; end;
{ get a pointer to the block we should return } { get a pointer to the block we should return }
result := pointer(pmc)+sizeof(tmemchunk_fixed_hdr); result := pointer(pmc)+sizeof(tmemchunk_fixed_hdr);
{ update freelist } { update freelist }
hp:=pmc^.next_fixed; pmc_next := pmc^.next_fixed;
poc := pmc^.poc; freelists_fixed[chunkindex] := pmc_next;
freelists_fixed[chunkindex] := hp; if assigned(pmc_next) then
if assigned(hp) then pmc_next^.prev_fixed := nil;
hp^.prev_fixed := nil; poc := poschunk(pointer(pmc) - (pmc^.size shr fixedoffsetshift));
if (poc^.used = 0) then if (poc^.used = 0) then
freelists_free_chunk[chunkindex] := false; freelists_free_chunk[chunkindex] := false;
inc(poc^.used); inc(poc^.used);
{ statistics } { statistics }
inc(internal_status.currheapused,size); inc(internal_status.currheapused,chunksize);
if internal_status.currheapused>internal_status.maxheapused then if internal_status.currheapused>internal_status.maxheapused then
internal_status.maxheapused:=internal_status.currheapused; internal_status.maxheapused:=internal_status.currheapused;
end; end;
function SysGetMem_Var(size: ptrint): pointer; function SysGetMem_Var(size: ptrint): pointer;
var var
pcurr : pmemchunk_var; pcurr : pmemchunk_var;
@ -994,23 +993,23 @@ end;
function SysFreeMem_Fixed(pmc: pmemchunk_fixed): ptrint; function SysFreeMem_Fixed(pmc: pmemchunk_fixed): ptrint;
var var
hp : pmemchunk_fixed; chunkindex,
chunksize, chunksize: ptrint;
chunkindex : ptrint; poc: poschunk;
poc : poschunk; pmc_next: pmemchunk_fixed;
begin begin
poc := pmc^.poc; chunksize := pmc^.size and fixedsizemask;
chunkindex:=poc^.chunkindex; dec(internal_status.currheapused, chunksize);
chunksize:=chunkindex shl blockshift;
{ statistics }
dec(internal_status.currheapused,chunksize);
hp:=freelists_fixed[chunkindex];
{ insert the block in it's freelist } { insert the block in it's freelist }
chunkindex := chunksize shr blockshift;
pmc_next := freelists_fixed[chunkindex];
pmc^.prev_fixed := nil; pmc^.prev_fixed := nil;
pmc^.next_fixed := hp; pmc^.next_fixed := pmc_next;
if assigned(hp) then if assigned(pmc_next) then
hp^.prev_fixed := pmc; pmc_next^.prev_fixed := pmc;
freelists_fixed[chunkindex] := pmc; freelists_fixed[chunkindex] := pmc;
{ decrease used blocks count }
poc := poschunk(pointer(pmc)-(pmc^.size shr fixedoffsetshift));
dec(poc^.used); dec(poc^.used);
if poc^.used <= 0 then if poc^.used <= 0 then
begin begin
@ -1019,47 +1018,44 @@ begin
HandleError(204); HandleError(204);
{ osblock can be freed? } { osblock can be freed? }
if freelists_free_chunk[chunkindex] then if freelists_free_chunk[chunkindex] then
append_to_oslist_fixed(poc) append_to_oslist_fixed(chunkindex, chunksize, poc)
else else
freelists_free_chunk[chunkindex] := true; freelists_free_chunk[chunkindex] := true;
end; end;
result := chunksize; result := chunksize;
end; end;
function SysFreeMem_Var(pmcv: pmemchunk_var): ptrint;
function SysFreeMem_Var(pcurr: pmemchunk_var): ptrint;
var var
chunksize: ptrint; chunksize: ptrint;
begin begin
chunksize := pcurr^.size and sizemask; chunksize := pmcv^.size and sizemask;
dec(internal_status.currheapused,chunksize); dec(internal_status.currheapused,chunksize);
{ insert the block in it's freelist } { insert the block in it's freelist }
pcurr^.size := pcurr^.size and (not usedflag); pmcv^.size := pmcv^.size and (not usedflag);
append_to_list_var(pcurr); append_to_list_var(pmcv);
pmcv := try_concat_free_chunk(pmcv);
if (pmcv^.size and (firstblockflag or lastblockflag)) = (firstblockflag or lastblockflag) then
append_to_oslist_var(pmcv);
result := chunksize; result := chunksize;
pcurr := try_concat_free_chunk(pcurr);
if (pcurr^.size and (firstblockflag or lastblockflag)) = (firstblockflag or lastblockflag) then
append_to_oslist_var(pcurr);
end; end;
function SysFreeMem(p: pointer): ptrint; function SysFreeMem(p: pointer): ptrint;
var var
hp : pmemchunk_fixed; pmc: pmemchunk_fixed;
begin begin
if p=nil then if p=nil then
begin begin
result:=0; result:=0;
exit; exit;
end; end;
pmc := pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr));
hp:=pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr));
{ check if this is a fixed- or var-sized chunk } { check if this is a fixed- or var-sized chunk }
if (hp^.size and fixedsizeflag) = 0 then if (pmc^.size and fixedsizeflag) = 0 then
result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr))) result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)))
else else
result := sysfreemem_fixed(hp); result := sysfreemem_fixed(pmc);
end; end;
{***************************************************************************** {*****************************************************************************
@ -1067,27 +1063,15 @@ end;
*****************************************************************************} *****************************************************************************}
Function SysFreeMemSize(p: pointer; size: ptrint):ptrint; Function SysFreeMemSize(p: pointer; size: ptrint):ptrint;
var
hp : pmemchunk_fixed;
begin begin
SysFreeMemSize := 0;
if p=nil then
exit;
if size<=0 then if size<=0 then
begin begin
if size<0 then if size<0 then
HandleError(204); HandleError(204);
exit; exit(0);
end; end;
{ can't free partial blocks, ignore size }
hp:=pmemchunk_fixed(p-sizeof(tmemchunk_fixed_hdr)); result := SysFreeMem(p);
{ check if this is a fixed- or var-sized chunk. We can't check the passed
size parameter since the block can be resized (by reallocmem) to an
optimized value that the user doesn't know }
if (hp^.size and fixedsizeflag) = 0 then
result := sysfreemem_var(pmemchunk_var(p-sizeof(tmemchunk_var_hdr)))
else
result := sysfreemem_fixed(hp);
end; end;
@ -1100,12 +1084,12 @@ begin
result := pmemchunk_fixed(pointer(p)-sizeof(tmemchunk_fixed_hdr))^.size; result := pmemchunk_fixed(pointer(p)-sizeof(tmemchunk_fixed_hdr))^.size;
if (result and fixedsizeflag) = 0 then if (result and fixedsizeflag) = 0 then
begin begin
result := SysMemSize and sizemask; result := result and sizemask;
dec(result, sizeof(tmemchunk_var_hdr)); dec(result, sizeof(tmemchunk_var_hdr));
end end
else else
begin begin
result := SysMemSize and fixedsizemask; result := result and fixedsizemask;
dec(result, sizeof(tmemchunk_fixed_hdr)); dec(result, sizeof(tmemchunk_fixed_hdr));
end; end;
end; end;
@ -1151,11 +1135,12 @@ begin
2. For resizing to greater size first check if the size fits in the fixed block range to prevent 2. For resizing to greater size first check if the size fits in the fixed block range to prevent
"truncating" the size by the fixedsizemask } "truncating" the size by the fixedsizemask }
if ((size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr))) and if ((size <= (maxblocksize - sizeof(tmemchunk_fixed_hdr))) and
((size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and sizemask<=currsize )) then ((size+sizeof(tmemchunk_fixed_hdr)+(blocksize-1)) and sizemask <= currsize)) then
begin begin
systryresizemem:=true; systryresizemem:=true;
exit; exit;
end; end;
{ we need to allocate a new fixed or var memchunck } { we need to allocate a new fixed or var memchunck }
exit; exit;
end; end;
@ -1289,6 +1274,7 @@ end;
{***************************************************************************** {*****************************************************************************
InitHeap InitHeap
*****************************************************************************} *****************************************************************************}
{$ifndef gba} {$ifndef gba}
{ This function will initialize the Heap manager and need to be called from { This function will initialize the Heap manager and need to be called from
the initialization of the system unit } the initialization of the system unit }
@ -1306,12 +1292,17 @@ end;
procedure FinalizeHeap; procedure FinalizeHeap;
var var
poc : poschunk; poc : poschunk;
pmc : pmemchunk_fixed;
i : longint; i : longint;
begin begin
{$ifdef HAS_SYSOSFREE} {$ifdef HAS_SYSOSFREE}
for i:=low(freelists_free_chunk) to high(freelists_free_chunk) do for i:=low(freelists_free_chunk) to high(freelists_free_chunk) do
if freelists_free_chunk[i] then if freelists_free_chunk[i] then
SysOSFree(freelists_fixed[i]^.poc,freelists_fixed[i]^.poc^.size); begin
pmc := freelists_fixed[i];
poc := poschunk(pointer(pmc)-(pmc^.size shr fixedoffsetshift));
SysOSFree(poc,poc^.size);
end;
while assigned(freeoslist) do while assigned(freeoslist) do
begin begin
poc:=freeoslist^.next; poc:=freeoslist^.next;