{FREE[FREELDR]

Improve the new heap to use a freelist, which boosts allocation performance by a factor of 7. Its now even slightly faster then bget.


svn path=/trunk/; revision=53989
This commit is contained in:
Timo Kreuzer 2011-10-04 14:55:23 +00:00
parent 38a67fd74f
commit 7c41ad4f0c

View file

@ -28,12 +28,23 @@ DBG_DEFAULT_CHANNEL(HEAP);
PVOID FrLdrDefaultHeap;
PVOID FrLdrTempHeap;
typedef struct _HEAP_BLOCK
typedef struct _BLOCK_HEADER
{
USHORT Size;
USHORT PreviousSize;
ULONG Tag;
UCHAR Data[];
} BLOCK_HEADER, *PBLOCK_HEADER;
typedef struct _BLOCK_DATA
{
ULONG Flink;
ULONG Blink;
} BLOCK_DATA, *PBLOCK_DATA;
typedef struct _HEAP_BLOCK
{
BLOCK_HEADER;
BLOCK_DATA Data[];
} HEAP_BLOCK, *PHEAP_BLOCK;
typedef struct _HEAP
@ -44,6 +55,9 @@ typedef struct _HEAP
ULONG NumAllocs;
ULONG NumFrees;
ULONG LargestAllocation;
ULONGLONG AllocationTime;
ULONGLONG FreeTime;
ULONG TerminatingBlock;
HEAP_BLOCK Blocks;
} HEAP, *PHEAP;
@ -59,6 +73,7 @@ HeapCreate(
TRACE("HeapCreate(MemoryType=%ld)\n", MemoryType);
/* Allocate some memory for the heap */
MaximumSize = ALIGN_UP_BY(MaximumSize, MM_PAGE_SIZE);
Heap = MmAllocateMemoryWithType(MaximumSize, MemoryType);
if (!Heap)
{
@ -68,7 +83,7 @@ HeapCreate(
}
/* Initialize the heap header */
Heap->MaximumSize = ALIGN_UP_BY(MaximumSize, MM_PAGE_SIZE);
Heap->MaximumSize = MaximumSize;
Heap->CurrentAllocBytes = 0;
Heap->MaxAllocBytes = 0;
Heap->NumAllocs = 0;
@ -79,8 +94,8 @@ HeapCreate(
Remaining = (MaximumSize - sizeof(HEAP)) / sizeof(HEAP_BLOCK);
TRACE("Remaining = %ld\n", Remaining);
/* Substract one for the terminating entry */
Remaining--;
/* Substract 2 for the terminating entry (header + free entry) */
Remaining -= 2;
Block = &Heap->Blocks;
PreviousSize = 0;
@ -92,6 +107,8 @@ HeapCreate(
Block->Size = (USHORT)min(MAXUSHORT, Remaining - 1);
Block->PreviousSize = PreviousSize;
Block->Tag = 0;
Block->Data[0].Flink = (Block - &Heap->Blocks) + Block->Size + 1;
Block->Data[0].Blink = (Block - &Heap->Blocks) - 1 - PreviousSize;
/* Substract current block size from remainder */
Remaining -= (Block->Size + 1);
@ -104,9 +121,13 @@ HeapCreate(
}
/* Now finish with a terminating block */
Heap->TerminatingBlock = Block - &Heap->Blocks;
Block->Size = 0;
Block->PreviousSize = PreviousSize;
Block->Tag = 'dnE#';
Block->Data[0].Flink = 0;
Block->Data[0].Blink = (Block - &Heap->Blocks) - 1 - PreviousSize;
Heap->Blocks.Data[0].Blink = Heap->TerminatingBlock;
return Heap;
}
@ -117,11 +138,11 @@ HeapDestroy(
{
PHEAP Heap = HeapHandle;
/* Mark all pages free */
/* Mark all pages as firmware temporary, so they are free for the kernel */
MmMarkPagesInLookupTable(PageLookupTableAddress,
(ULONG_PTR)Heap / MM_PAGE_SIZE,
Heap->MaximumSize / MM_PAGE_SIZE,
LoaderFree);
LoaderFirmwareTemporary);
}
VOID
@ -173,7 +194,7 @@ HeapRelease(
if (Block->Size == 0) break;
}
TRACE("HeapRelease() done, freed %ld pages\n", AllFreePages);
ERR("HeapRelease() done, freed %ld pages\n", AllFreePages);
}
VOID
@ -182,17 +203,20 @@ HeapCleanupAll(VOID)
PHEAP Heap;
Heap = FrLdrDefaultHeap;
TRACE("Heap statistics for default heap:\n"
ERR("Heap statistics for default heap:\n"
"CurrentAlloc=0x%lx, MaxAlloc=0x%lx, LargestAllocation=0x%lx\n"
"NumAllocs=%ld, NumFrees=%ld\n",
Heap->CurrentAllocBytes, Heap->MaxAllocBytes, Heap->LargestAllocation,
Heap->NumAllocs, Heap->NumFrees);
ERR("AllocTime = %I64d, FreeTime = %I64d, sum = %I64d\n",
Heap->AllocationTime, Heap->FreeTime, Heap->AllocationTime + Heap->FreeTime);
/* Release fre pages */
HeapRelease(FrLdrDefaultHeap);
Heap = FrLdrTempHeap;
TRACE("Heap statistics for temp heap:\n"
ERR("Heap statistics for temp heap:\n"
"CurrentAlloc=0x%lx, MaxAlloc=0x%lx, LargestAllocation=0x%lx\n"
"NumAllocs=%ld, NumFrees=%ld\n",
Heap->CurrentAllocBytes, Heap->MaxAllocBytes, Heap->LargestAllocation,
@ -202,6 +226,46 @@ HeapCleanupAll(VOID)
HeapDestroy(FrLdrTempHeap);
}
static VOID
HeapRemoveFreeList(
PHEAP Heap,
PHEAP_BLOCK Block)
{
PHEAP_BLOCK Previous, Next;
Next = &Heap->Blocks + Block->Data[0].Flink;
Previous = &Heap->Blocks + Block->Data[0].Blink;
ASSERT((Next->Tag == 0) || (Next->Tag == 'dnE#'));
ASSERT(Next->Data[0].Blink == Block - &Heap->Blocks);
ASSERT((Previous->Tag == 0) || (Previous->Tag == 'dnE#'));
ASSERT(Previous->Data[0].Flink == Block - &Heap->Blocks);
Next->Data[0].Blink = Previous - &Heap->Blocks;
Previous->Data[0].Flink = Next - &Heap->Blocks;
}
static VOID
HeapInsertFreeList(
PHEAP Heap,
PHEAP_BLOCK FreeBlock)
{
PHEAP_BLOCK ListHead, NextBlock;
ASSERT(FreeBlock->Tag == 0);
/* Terminating block serves as free list head */
ListHead = &Heap->Blocks + Heap->TerminatingBlock;
for (NextBlock = &Heap->Blocks + ListHead->Data[0].Flink;
NextBlock < FreeBlock;
NextBlock = &Heap->Blocks + NextBlock->Data[0].Flink);
FreeBlock->Data[0].Flink = NextBlock - &Heap->Blocks;
FreeBlock->Data[0].Blink = NextBlock->Data[0].Blink;
NextBlock->Data[0].Blink = FreeBlock - &Heap->Blocks;
NextBlock = &Heap->Blocks + FreeBlock->Data[0].Blink;
NextBlock->Data[0].Flink = FreeBlock - &Heap->Blocks;
}
PVOID
HeapAllocate(
PVOID HeapHandle,
@ -210,7 +274,8 @@ HeapAllocate(
{
PHEAP Heap = HeapHandle;
PHEAP_BLOCK Block, NextBlock;
USHORT BlockSize, PreviousSize = 0, Remaining;
USHORT BlockSize, Remaining;
ULONGLONG Time = __rdtsc();
/* Check if the allocation is too large */
if ((ByteSize + sizeof(HEAP_BLOCK)) > MAXUSHORT * sizeof(HEAP_BLOCK))
@ -225,16 +290,13 @@ HeapAllocate(
/* Calculate alloc size */
BlockSize = (USHORT)((ByteSize + sizeof(HEAP_BLOCK) - 1) / sizeof(HEAP_BLOCK));
/* Loop all heap chunks */
for (Block = &Heap->Blocks;
/* Walk the free block list */
Block = &Heap->Blocks + Heap->TerminatingBlock;
for (Block = &Heap->Blocks + Block->Data[0].Flink;
Block->Size != 0;
Block = Block + 1 + Block->Size)
Block = &Heap->Blocks + Block->Data[0].Flink)
{
ASSERT(Block->PreviousSize == PreviousSize);
PreviousSize = Block->Size;
/* Continue, if its not free */
if (Block->Tag != 0) continue;
ASSERT(Block->Tag == 0);
/* Continue, if its too small */
if (Block->Size < BlockSize) continue;
@ -242,6 +304,9 @@ HeapAllocate(
/* This block is just fine, use it */
Block->Tag = Tag;
/* Remove this entry from the free list */
HeapRemoveFreeList(Heap, Block);
/* Calculate the remaining size */
Remaining = Block->Size - BlockSize;
@ -259,6 +324,7 @@ HeapAllocate(
NextBlock->Size = Remaining - 1;
NextBlock->PreviousSize = BlockSize;
BlockSize = NextBlock->Size;
HeapInsertFreeList(Heap, NextBlock);
/* Advance to the next block */
NextBlock = NextBlock + 1 + BlockSize;
@ -281,6 +347,7 @@ HeapAllocate(
Heap->MaxAllocBytes = max(Heap->MaxAllocBytes, Heap->CurrentAllocBytes);
Heap->LargestAllocation = max(Heap->LargestAllocation,
Block->Size * sizeof(HEAP_BLOCK));
Heap->AllocationTime += (__rdtsc() - Time);
TRACE("HeapAllocate(%p, %ld, %.4s) -> return %p\n",
HeapHandle, ByteSize, &Tag, Block->Data);
@ -303,6 +370,7 @@ HeapFree(
PHEAP Heap = HeapHandle;
PHEAP_BLOCK Block, PrevBlock, NextBlock;
USHORT PreviousSize = 0;
ULONGLONG Time = __rdtsc();
TRACE("HeapFree(%p, %p)\n", HeapHandle, Pointer);
ASSERT(Tag != 'dnE#');
@ -317,10 +385,10 @@ HeapFree(
Block = ((PHEAP_BLOCK)Pointer) - 1;
/* Check if the tag matches */
if (Tag && (Block->Tag != Tag))
if ((Tag && (Block->Tag != Tag)) || (Block->Tag == 0))
{
ERR("HEAP: Bad tag! Pointer = %p: block tag '%.4s', requested '%.4s'\n",
Pointer, &Block->Tag, &Tag);
ERR("HEAP: Bad tag! Pointer=%p: block tag '%.4s', requested '%.4s', size=0x%lx\n",
Pointer, &Block->Tag, &Tag, Block->Size);
ASSERT(FALSE);
}
@ -336,29 +404,34 @@ HeapFree(
NextBlock = Block + Block->Size + 1;
/* Check if next block is free */
if (NextBlock->Tag == 0)
if ((NextBlock->Tag == 0) &&
((Block->Size + NextBlock->Size + 1) <= MAXUSHORT))
{
/* Check if their combined size if small enough */
if ((Block->Size + NextBlock->Size + 1) <= MAXUSHORT)
{
/* Merge next block into current */
Block->Size += NextBlock->Size + 1;
NextBlock = Block + Block->Size + 1;
NextBlock->PreviousSize = Block->Size;
}
/* Merge next block into current */
Block->Size += NextBlock->Size + 1;
HeapRemoveFreeList(Heap, NextBlock);
NextBlock = Block + Block->Size + 1;
}
/* Check if there is a block before and it's free */
if ((Block->PreviousSize != 0) && (PrevBlock->Tag == 0))
if ((Block->PreviousSize != 0) && (PrevBlock->Tag == 0) &&
((PrevBlock->Size + Block->Size + 1) <= MAXUSHORT))
{
/* Check if their combined size if small enough */
if ((PrevBlock->Size + Block->Size + 1) <= MAXUSHORT)
{
/* Merge current block into previous */
PrevBlock->Size += Block->Size + 1;
NextBlock->PreviousSize = PrevBlock->Size;
}
/* Merge current block into previous */
PrevBlock->Size += Block->Size + 1;
Block = PrevBlock;
}
else
{
/* Insert the entry into the free list */
HeapInsertFreeList(Heap, Block);
}
/* Update the next block's back link */
NextBlock->PreviousSize = Block->Size;
Heap->FreeTime += (__rdtsc() - Time);
}