Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions src/gpgmm/common/SlabBlockAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@

namespace gpgmm {

struct Slab;

// SlabBlock keeps a reference back to the slab to avoid creating a copy of the block with the
// slab being allocated from.
struct SlabBlock : public MemoryBlock {
SlabBlock* pNext = nullptr;
Slab* pSlab = nullptr;
};

// SlabBlockAllocator uses the slab allocation technique to satisfy an
// a block-allocation request. A slab consists of contiguious memory carved up into
// fixed-size blocks (also called "pages" or "chunks"). The slab allocator
Expand All @@ -40,10 +49,6 @@ namespace gpgmm {
const char* GetTypename() const override;

private:
struct SlabBlock : public MemoryBlock {
SlabBlock* pNext = nullptr;
};

struct BlockList {
SlabBlock* pHead = nullptr; // First free block in slab.
};
Expand Down
56 changes: 41 additions & 15 deletions src/gpgmm/common/SlabMemoryAllocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,36 @@ namespace gpgmm {
// emitted.
constexpr static double kPrefetchCoverageWarnMinThreshold = 0.50;

// Slab is a node in a doubly-linked list that contains a free-list of blocks
// and a reference to underlying memory.
struct Slab : public LinkNode<Slab>, public RefCounted {
Slab(uint64_t blockCount, uint64_t blockSize)
: RefCounted(0), Allocator(blockCount, blockSize) {
}

~Slab() {
if (IsInList()) {
RemoveFromList();
}
}

uint64_t GetBlockCount() const {
return Allocator.GetBlockCount();
}

bool IsFull() const {
return static_cast<uint32_t>(GetRefCount()) == Allocator.GetBlockCount();
}

double GetUsedPercent() const {
return static_cast<uint32_t>(GetRefCount()) /
static_cast<double>(Allocator.GetBlockCount());
}

SlabBlockAllocator Allocator;
std::unique_ptr<MemoryAllocation> SlabMemory;
};

// SlabMemoryAllocator

SlabMemoryAllocator::SlabMemoryAllocator(uint64_t blockSize,
Expand Down Expand Up @@ -311,30 +341,28 @@ namespace gpgmm {
pCache->FullList.push_front(pFreeSlab);
}

// Wrap the block in the containing slab. Since the slab's block could reside in another
// allocated block, the slab's allocation offset must be made relative to slab's underlying
// memory and not the slab.
BlockInSlab* blockInSlab = new BlockInSlab();
blockInSlab->pBlock = subAllocation->GetBlock();
// Assign the containing slab to the block so DeallocateMemory() knows how to release it.
SlabBlock* blockInSlab = static_cast<SlabBlock*>(subAllocation->GetBlock());
blockInSlab->pSlab = pFreeSlab;
blockInSlab->Size = subAllocation->GetBlock()->Size;
blockInSlab->Offset =
pFreeSlab->SlabMemory->GetOffset() + subAllocation->GetBlock()->Offset;

// Since the slab's block could reside in another allocated block, the allocation
// offset must be made relative to the slab's underlying memory and not the slab itself.
const uint64_t offsetFromMemory = pFreeSlab->SlabMemory->GetOffset() + blockInSlab->Offset;

mInfo.UsedBlockCount++;
mInfo.UsedBlockUsage += blockInSlab->Size;

return std::make_unique<MemoryAllocation>(
this, subAllocation->GetMemory(), blockInSlab->Offset, AllocationMethod::kSubAllocated,
blockInSlab, request.SizeInBytes);
return std::make_unique<MemoryAllocation>(this, subAllocation->GetMemory(),
offsetFromMemory, AllocationMethod::kSubAllocated,
blockInSlab, request.SizeInBytes);
}

void SlabMemoryAllocator::DeallocateMemory(std::unique_ptr<MemoryAllocation> subAllocation) {
TRACE_EVENT0(TraceEventCategory::Default, "SlabMemoryAllocator.DeallocateMemory");

std::lock_guard<std::mutex> lock(mMutex);

const BlockInSlab* blockInSlab = static_cast<BlockInSlab*>(subAllocation->GetBlock());
SlabBlock* blockInSlab = static_cast<SlabBlock*>(subAllocation->GetBlock());
ASSERT(blockInSlab != nullptr);

Slab* slab = blockInSlab->pSlab;
Expand All @@ -350,9 +378,7 @@ namespace gpgmm {
mInfo.UsedBlockCount--;
mInfo.UsedBlockUsage -= blockInSlab->Size;

MemoryBlock* block = blockInSlab->pBlock;
slab->Allocator.DeallocateBlock(block);
SafeDelete(blockInSlab);
slab->Allocator.DeallocateBlock(blockInSlab);

MemoryBase* slabMemory = subAllocation->GetMemory();
ASSERT(slabMemory != nullptr);
Expand Down
38 changes: 0 additions & 38 deletions src/gpgmm/common/SlabMemoryAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,44 +72,6 @@ namespace gpgmm {

bool IsPrefetchCoverageBelowThreshold() const;

// Slab is a node in a doubly-linked list that contains a free-list of blocks
// and a reference to underlying memory.
struct Slab : public LinkNode<Slab>, public RefCounted {
Slab(uint64_t blockCount, uint64_t blockSize)
: RefCounted(0), Allocator(blockCount, blockSize) {
}

~Slab() {
ASSERT(SlabMemory == nullptr);
if (IsInList()) {
RemoveFromList();
}
}

uint64_t GetBlockCount() const {
return Allocator.GetBlockCount();
}

bool IsFull() const {
return static_cast<uint32_t>(GetRefCount()) == Allocator.GetBlockCount();
}

double GetUsedPercent() const {
return static_cast<uint32_t>(GetRefCount()) /
static_cast<double>(Allocator.GetBlockCount());
}

SlabBlockAllocator Allocator;
std::unique_ptr<MemoryAllocation> SlabMemory;
};

// Stores a reference back to the slab containing the block so DeallocateMemory
// knows which slab (and block allocator) to use.
struct BlockInSlab : public MemoryBlock {
MemoryBlock* pBlock = nullptr;
Slab* pSlab = nullptr;
};

// Group of one or more slabs of the same size.
struct SlabCache {
SizedLinkedList<Slab> FreeList; // Slabs that contain partial or empty
Expand Down