llvm::BumpPtrAllocator
源码分析
一、概述
最近开始捣鼓一个玩具 C 语言编译器项目,打算不借助编译器相关的库,写一个基本完整的编译器。
为了对各种类型、声明、语句、表达式等大量的较小对象快速分配内存,打算写一个内存池分配器来解决。毕竟不能闭门造车,这里参考 Clang 的解决方案,使用了 bump-pointer allocator 来分配这些对象的内存。
之前没有听说过 bump-pointer allocator,在网上搜索了一番,找到 CS107 Lecture14 这个 PPT,对 bump-pointer allocator 有了一个大概的认知。bump-pointer allocator 仅在 allocate 时分配下一个可用内存地址,在 deallocate 时不执行任何操作。在 bump-pointer allocator 生命期结束时,回收所有分配出去的内存。
接下来对 llvm::BumpPtrAllocator
进行源码分析。
二、源码分析
下文代码主要参考 llvm 12.0.0 版本的代码。在 include/llvm/Support/Allocator.h
中,可以找到 llvm::BumpPtrAllocator
的定义:
typedef BumpPtrAllocatorImpl<> BumpPtrAllocator;
|
接下来跳转到 BumpPtrAllocatorImpl
中,首先列出一些接口方法以及主要成员变量:
template <typename AllocatorT = MallocAllocator, size_t SlabSize = 4096, size_t SizeThreshold = SlabSize, size_t GrowthDelay = 128> class BumpPtrAllocatorImpl : public AllocatorBase<BumpPtrAllocatorImpl<AllocatorT, SlabSize, SizeThreshold, GrowthDelay>>, private AllocatorT { public: BumpPtrAllocatprImpl() = default; ~BumpPtrAllocatprImpl(); void *Allocate(size_t Size, Align Aligment); void Deallocate(const void *Ptr, size_t Size, size_t ); private:
char *CurPtr = nullptr; char *End = nullptr; SmallVector<void *, 4> Slabs; SmallVector<std::pair<void *, size_t>, 0> CustomSizedSlabs; size_t BytesAllocated = 0;
size_t RedZoneSize = 1; }
|
先从简单的开始看,因为在 BumpPtrAllocator
生命周期内永远不会释放内存,所以 Deallocate
什么都不做:
void Deallocate(const void *Ptr, size_t Size, size_t ) { }
|
接下来看 Allocate
方法:
void *Allocate(size_t Size, Align Aligment) { BytesAllocated += Size;
size_t Adjustment = offsetToAlignedAddr(CurPtr, Aligment); size_t SizeToAllocate = Size;
if (Adjustment + SizeToAllocate <= size_t(End - CurPtr)) { char *AlignedPtr = CurPtr + Adjustment; CurPtr = AlignedPtr + SizeToAllocate; return AlignedPtr; }
size_t PaddedSize = SizeToAllocate + Alignment.value() - 1; if (PaddedSize > SizeThreshold) { void *NewSlab = AllocateT::Allocate(PaddedSize, alignof(std::max_align_t)); CustomSizedSlabs.push_back(std::make_pair(NewSlab, PaddedSize));
uintptr_t AlignedAddr = alignAddr(NewSlab, Alignment); char *AlignedPtr = (char*)AlignedAddr; return AlignedPtr; }
StartNewSlab(); uintptr_t AlignedAddr = alignAddr(NewSlab, Alignment); char *AlignedPtr = (char*)AlignedAddr; CurPtr = AlignedPtr + SizeToAllocate; return AlignedPtr; }
void StartNewSlab() { size_t AllocatedSlabSize = computeSlabSize(Slabs.size()); void *NewSlab = AllocatorT::Allocate(AllocatedSlabSize, alignof(std::max_align_t)); Slabs.push_back(NewSlab); CurPtr = (char *)(NewSlab); End = ((char *)NewSlab) + AllocatedSlabSize; }
|
最后看一下析构函数:
~BumpPtrAllocatprImpl() { DeallocateSlabs(Slabs.begin(), Slabs.end()); DeallocateCustomSizedSlabs(); }
|
实现起来非常简单,虽然浪费了一些内存,但是速度非常快。对于我们的场景使用 BumpPtrAllocator
非常合适。