"花,就在火海里摇曳开着。"
一、技术介绍
(1)什么是池化技术?
池 是在计算机技术中经常使用的一种设计模式 ,其内涵在于:将程序中需要经常使用的核心资源先申请出来,放到一个池内,由程序自己管理。这样可以提高资源的使用效率(不用频繁向系统申请、释放资源),也可以保证本程 序占有的资源数量。
(2)内存池
内存池(Memory Pool) 是一种动态内存分配与管理技术。通常情况下,程序员习惯直接使用 new、delete、malloc、free等API(封装函数)申请分配和释放内存,这样导致的后果是:当程序长时间 运行时,由于所申请内存块的大小不定, 频繁使用时会造成大量的内存碎片从而降低程序和操作系统的性能。内存池则是在真正使用内存之前:①先申请分配一大块内存(内存池)留作备用, 当程序 员申请内存时,从池中取出一块动态分配,当程序员释放内存时,将释放的内存再放入池内,再 次申请池可以 再取出来使用。②解决内存碎片尽量与周边的空闲内存块合③若内存池不够时,则自动扩大内存池,从操作系统中申请更大的内存池。
如何理解内存碎片问题?
外碎片:申请内存不连续的问题
内碎片:对齐方式导致的空间浪费
二、小试牛刀
在开始tcmalloc之前,还是先上一道"开胃菜"——定长内存池分配器
所谓定长内存池,就是在申请资源之前,已经将空间开好了。 而不是直接向系统要内存块。
即:实现一个memory指向一大块申请好的内存池。 同时用freelist用于管理释放 回来的小块内存。
定长内存池的优点很明显:实现简单,分配和释放效率都达到了极致
不足点在于:功能单一,只能够解决定长的需求。同时占用着内存空间。
(1)类设计
定长内存池的可以是上面的两种。这里我就选取第二种。
注:这里设计指向大块内存
(2)申请空间
但是其中仍然没有脱离malloc,即便malloc也有自己的内存池。在WINDOWS下 有自己专门向堆申请空间的系统调用函数。
static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32
//virtualAlloc //转换为字节
void* ptr = VirtualAlloc(0, kpage << 13, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
//linux _brk
#endif
if (ptr == nullptr)
{
throw std::bad_alloc();
}
return ptr;
}
T* New()
{
T* obj = nullptr;
if (_FreeList)
{
void* nextObj = *((void**)_FreeList);
obj = (T*)_FreeList;
_FreeList = nextObj;
}
else
{
if (_RemainSize < sizeof(T))
{
_RemainSize = 128 * 1024;
//void* ptr = malloc(_RemainSize);
//128 / 8
void* ptr = SystemAlloc(16);
if (ptr == nullptr)
{
throw std::bad_alloc();
}
_Memory =(char*)ptr;
}
size_t ObjectSize = sizeof(T*) > sizeof(T) ? sizeof(T*) : sizeof(T);
obj =(T*) _Memory;
_Memory += ObjectSize;
_RemainSize -= ObjectSize;
}
new(obj) T;
return obj;
}
(3)释放空间
释放空间更为简单 就只是做做简单的头插!
void Delete(T* obj)
{
obj->~T();
*(void**)obj = _FreeList;
_FreeList = obj;
}
(4)性能对比
struct TreeNode
{
int _val;
TreeNode* _left;
TreeNode* _right;
TreeNode()
:_val(0)
, _left(nullptr)
, _right(nullptr)
{}
};
void TestObjectPool()
{
// 申请释放的轮次
const size_t Rounds = 10;
// 每轮申请释放多少次
const size_t N = 100000;
std::vector<TreeNode*> v1;
v1.reserve(N);
//普通的new
size_t begin1 = clock();
for (size_t j = 0; j < Rounds; ++j)
{
for (int i = 0; i < N; ++i)
{
v1.push_back(new TreeNode);
}
for (int i = 0; i < N; ++i)
{
delete v1[i];
}
v1.clear();
}
size_t end1 = clock();
//内存池的new
std::vector<TreeNode*> v2;
v2.reserve(N);
ObjectPool<TreeNode> TNPool;
size_t begin2 = clock();
for (size_t j = 0; j < Rounds; ++j)
{
for (int i = 0; i < N; ++i)
{
v2.push_back(TNPool.New());
}
for (int i = 0; i < N; ++i)
{
TNPool.Delete(v2[i]);
}
v2.clear();
}
size_t end2 = clock();
cout << "new cost time:" << end1 - begin1 << endl;
cout << "object pool cost time:" << end2 - begin2 << endl;
}
我们来看看,测试结果;
三、TCMALLOC
(1)TCMALLOC介绍
TCMalloc 是 Google 自定义的 c 的 malloc () 和 c + + 操作符 new 的实现,用于 c 和 c + + 代码中的内存分配。
TCMalloc 将 c 的 malloc () 和 c + + 操作符 new 的内部实现替换为 TCMalloc 的实现,开发者只需编译链接 TCMalloc 的静态库或动态库即可,无需改动任何与内存分配有关的代码。
TCMalloc 通常被用于提高内存分配的性能,实现了高效的内存管理。
四、申请流程
(1)ThreadCache
①ThreadCache设计与声明:
1. 当内存申请size<=256KB时在thread cache中申请内存,计算size在自由链表中的位置,如果自由链表中有内存对象时,直接从FistList[i]中Pop一下对象,时间复杂度是O(1),且没有锁竞争。
2. 当FreeList[i]中没有对象时,则批量从central cache中获取一定数量的对象,插入到自由链
表并返回一个对象。
FreeList;
内存对齐:
对齐规则:
映射规则:
上面两个函数都是 各个文件用到的。 因此将他们放在comm.h里,其次因为反复用到频繁调用缘故,这里把它们设置为 内联+静态函数(不需要对象调用)。
由此,我们终于有足够的铺垫,设计ThreadCache;
如果不知道TLS的可以去看看下面的文章,或许对你有帮助。
TLS静态https://blog.csdn.net/evilswords/article/details/8191230TLS动态https://blog.csdn.net/yusiguyuan/article/details/22938671
其作用就是,让在ThreadCache内申请内存时,是无锁的状态。
class ThreadCache
{
public:
void* Allocate(size_t size);
void Delallocate(void* ptr,size_t size);
void* FetchFromCentralCache(size_t index, size_t size);
private:
FreeList _freelist[NFREELIST];
};
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;
//这里使用的静态版的
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;
②ConcurrentAlloc
我们不会直接暴露ThreadCache给外面。因此我们在调用tcmalloc的时候 会进行一层封装。
void* ConcurrentAlloc(size_t size)
{
if (pTLSThreadCache == nullptr)
{
pTLSThreadCache = new ThreadCache; //创建ThreadCache
}
return pTLSThreadCache->Allocate(size); //申请内存
}
//我们在使用free 的时候 都没有带size
//这里我们就先带size 后面会有方法解决这个问题的!
void ConcurrentDelAlloc(void* ptr,size_t size)
{
assert(pTLSThreadCache);
pTLSThreadCache->Delallocate(ptr, size);
}
③ThreadCache实现
void* ThreadCache::Allocate(size_t size)
{
assert(size < MAX_BYTES);
size_t alignSize = SizeClass::RoundUP(size);
size_t index = SizeClass::Index(size);
if (!_freelist[index].Empty())
{
return _freelist[index].Pop();
}
return FetchFromCentralCache(index, alignSize);
}
void ThreadCache::Delallocate(void* ptr, size_t size)
{
assert(ptr);
size_t index = SizeClass::Index(size);
_freelist[index].Push(ptr);
}
(2)CentralCache
①CentralCache的设计与声明
1.当thread cache中没有内存时,就会 "批量" 向central cache申请一些内存对象,central cache也有一个哈希映射的freelist(与ThreadCache哈希键值一一对应),freelist中挂着span,从span中取出对象给thread cache,这个过程是需要加锁的。
2.central cache中没有非空的span时,则将空的span链在一起,向page cache申请一个span
对象,span对象中是一些以页为单位的内存,切成需要的内存大小,并链接起来,挂到span中。
Span;
SpanList:
class SpanList { public: SpanList() { _head = new Span; _head->_next = _head; _head->_prev = _head; } Span* Begin() { return _head->_next; } Span* End() { return _head; } void Insert(Span* pos,Span* newSpan) { assert(pos); assert(newSpan); Span* prev = pos->_prev; prev->_next = newSpan; newSpan->_prev = prev; newSpan->_next = pos; pos->_prev = newSpan; } void Erase(Span* pos) { assert(pos); assert(pos != _head); Span* prev = pos->_prev; Span* next = pos->_next; prev->_next = next; next->_prev = prev; } private: Span* _head; public: std::mutex _mtx; };
单例模式:
保证CentralCache 全局只有唯一确定的一个。包括之后的PageCache 也是采用单例模式。
封掉 该类的构造和拷贝构造! 并提供一个static 成员函数 调用全局唯一静态变量
class CentralCache { public: static CentralCache* GetInStance() { return &_Inst; } Span* GetOneSpan(SpanList& _list,size_t size); size_t FetchFromRange(void*& start,void*& end,size_t batchNum,size_t size); private: SpanList _spanlist[NFREELIST]; static CentralCache _Inst; CentralCache(){} CentralCache(const CentralCache&) = delete; };
②CentralCache实现
ThreadCache 与 CentralCache的连通器(FetchFromCentralCache):
理解慢增长:
void* ThreadCache::FetchFromCentralCache(size_t index, size_t size) { size_t batchNum = (std::min)(SizeClass::NumMoveSize(size),_freelist[index].MaxSize()); if (batchNum == _freelist[index].MaxSize()) { _freelist[index].MaxSize() += 1; } void* start = nullptr; void* end = nullptr; size_t acltualNum = CentralCache::GetInStance()->FetchFromRange(start, end, batchNum, size); assert(acltualNum > 0); if (acltualNum == 1) { assert(start == end); } else { _freelist[index].PushRange(NextObj(start), end, acltualNum - 1); } return start; }
FetchRangeObj:
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size) { size_t index = SizeClass::Index(size); _spanlist[index]._mtx.lock(); Span* span = GetOneSpan(_spanlist[index],size); assert(span); assert(span->_freelist); start = span->_freelist; end = start; size_t i = 0; size_t actulNum = 1; while (i < batchNum - 1 && NextObj(end)) { end = NextObj(end); i++; actulNum++; } span->_freelist = NextObj(end); NextObj(end) = nullptr; _spanlist[index]._mtx.unlock(); return actulNum; }
GetOneSpan;
Span* CentralCache::GetOneSpan(SpanList& _list, size_t size) { Span* it = _list.Begin(); while (it != _list.End()) { if (it->_freelist) { return it; } it = it->_next; } //为什么这里需要解锁? 因为在外面已经有一个桶锁了 //此时这个线程走到这一步 已经不在CentralCache 而是要访问PageCache //而PageCache是需要加大锁的(之后会说)。 //否则如果不释放锁 那么其他要在这个位置归还span的线程 也会处在阻塞中 _list._mtx.unlock(); //...这里之后又是牵涉到PageCache了 那也之后再讲 }
(3)PageCache
①PageCache的设计与声明
当central cache向page cache申请内存时,page cache先检查对应位置有没有span,如果
没有则向更大页寻找一个span,如果找到则分裂成两个。比如:申请的是4page,4page后
面没有挂span,则向后面寻找更大的span,假设在10page位置找到一个span,则将10page span分裂为一个4page span和一个6page span。PageCache遵循的原则就是 大切小!
单例模式:
class PageCache { public: static PageCache* GetInstance() { return &_Inst; } void* NewSpan(size_t k); //在PageCache下 也需要有锁 std::mutex _pagemtx; private: SpanList _spanlist[NPAGES]; static PageCache _Inst; PageCache() {} PageCache(const PageCache&) = delete; };
②PageCache实现
字节转换为页:
GetOneSpan;
Span* CentralCache::GetOneSpan(SpanList& _list, size_t size) { Span* it = _list.Begin(); while (it != _list.End()) { if (it->_freelist) { return it; } it = it->_next; } _list._mtx.unlock(); PageCache::GetInstance()->_pagemtx.lock(); Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size)); PageCache::GetInstance()->_pagemtx.unlock(); char* start = (char*)(span->_pageId <<PAGE_SHIFT); size_t bytes = (span->n << PAGE_SHIFT); char* end = start + bytes; span->_freelist = start; start += size; void* tail = span->_freelist; while (start < end) { NextObj(tail) = start; //tail = start; tail = NextObj(tail); start += size; } NextObj(tail) = nullptr; //访问该SpanList 加锁 _list._mtx.lock(); _list.PushFront(span); return span; }
NewSpan;
Span* PageCache::NewSpan(size_t k) { assert(k > 0); if (!_spanlist[k].Empty()) { return _spanlist[k].PopFront(); } for (size_t i = k +1;k < NPAGES;++i) { if (!_spanlist[i].Empty()) { Span* nSpan = _spanlist[i].PopFront(); Span* kSpan = new Span; kSpan->_pageId = nSpan->_pageId; kSpan->n = k; nSpan->_pageId += k; nSpan->n -= k; _spanlist[nSpan->n].PushFront(nSpan); } } Span* bigSpan = new Span; void* ptr = SystemAlloc(NPAGES - 1); bigSpan->_pageId = ((PAGE_ID)ptr >> PAGE_SHIFT); bigSpan->n = NPAGES - 1; _spanlist[bigSpan->n].PushFront(bigSpan); return NewSpan(k); }
五、释放流程
ConcurrentFree:
当然,我们在使用free、delete的时候 ,仅仅只需要指向这块地方的指针即可。那这个问题会放在后面解决。
ThreadCache的释放流程;
1. 当释放内存小于64k时将内存释放回thread cache,计算size在自由链表中的位置,将对象Push到FreeList[i].
2. 当链表的长度过长,则回收一部分内存对象到central cache。(这是为了最后解决外碎片的问题)void ThreadCache::Delallocate(void* ptr, size_t size) { assert(ptr); size_t index = SizeClass::Index(size); _freelist[index].Push(ptr); if (_freelist[index].MaxSize() < _freelist[index].Size()) { ListTooLong(_freelist[index], _freelist[index].MaxSize()); } } void ThreadCache::ListTooLong(FreeList& list, size_t size) { void* start = nullptr; void* end = nullptr; list.PopRange(start, end, size); //...向CentralCache这一层归还span }
CentralCache释放流程;
当thread_cache过长或者线程销毁,则会将内存释放回central cache中的,释放回来时--
use_count。当use_count减到0时则表示所有对象都回到了span,则将span释放回page
cache,page cache中会对前后相邻的空闲页进行合并。CentralCache是将一个完整的Span切割为小份。每次拿一块内存出去,可以用_useCount计数;
void CentralCache::ReleaseListToSpans(void* start, size_t size) { size_t index = SizeClass::Index(size); _spanlist[index]._mtx.lock(); while (start) { void* next = NextObj(start); Span* span = PageCache::GetInstance()->MapObjectToSpan(start); NextObj(start) = span->_freelist; span->_freelist = start; span->_UseCount--; if (span->_UseCount == 0) { _spanlist[index].Erase(span); span->_freelist = nullptr; span->_next = nullptr; span->_prev = nullptr; _spanlist[index]._mtx.unlock(); //返还给下层PageCahce } start = next; } _spanlist[index]._mtx.unlock(); }
PageCache释放流程;
如果central cache释放回一个span,则依次寻找span的前后page id的span,看是否可以
合并,如果合并继续向前寻找。这样就可以将切小的内存合并收缩成大的span,减少内存
碎片。void PageCache::ReleaseSpantoPageCache(Span* span) { assert(span); while (1) { PAGE_ID prevID = span->_pageId - 1; auto ret = _idMapSpan.find(prevID); if (ret == _idMapSpan.end()) { break; } Span* prevSpan = ret->second; if (prevSpan->n + span->n> NPAGES) { break; } if (prevSpan->_IsUse == true) { break; } span->_pageId = prevSpan->_pageId; span->n += prevSpan->n; _spanlist[prevSpan->n].Erase(prevSpan); delete prevSpan; } while (1) { PAGE_ID nextId = span->_pageId + span->n; auto ret = _idMapSpan.find(nextId); if (ret == _idMapSpan.end()) { break; } Span* nextSpan = ret->second; if (nextSpan->_IsUse == true) break; if (nextSpan->n + span->n > NPAGES) break; span->n += nextSpan->n; _spanlist[nextSpan->n].Erase(nextSpan); delete nextSpan; } _spanlist[span->n].PushFront(span); span->_IsUse = false; _idMapSpan[span->_pageId] = span; _idMapSpan[span->_pageId + span->n - 1] = span; }
但是别忘了 _idMapSpan在newSpan 的时候 就应当建立映射;
解决ConCurrentFree传size的问题;
解决大块内存申请的问题:
如果大小 <= 256 KB 那么这个内存仍然起着作用!
但是如果 > 256KB 呢? 解决这个大块内存问题该怎么做?
申请:
释放:
解决delete、new问题;
在span 的时候 多数用到new delete 但是我们并没有很好地进行分离。
在最早的时候,我们写的定长内存池此时就起了作用了。
在用到new delete的地方替换即可
基本的释放+申请的流程就走完了。第一要做的 肯定是看有bug没有?
六、测试与性能瓶颈
(1)调试
线程安全问题:
在访问_idMapSpan的时候 也存在线程安全问题。 因为STL不保证线程安全。当你去读取的时候,也有看其他线程在翻转。因此 需要函数内同样需要加锁。
为什么PageCache + 大锁 CentralCache+桶锁?
因为PageCache牵涉到 前后页合并的问题。+桶锁不能避免线程安全。 而CentralCache只是拿到index 去访问这个桶的span。其他线程去访问其他桶 是完全不背离的。
(2)性能瓶颈
????什么牛马! 自己写的tcmalloc还不如 malloc自己。
我们可以来看看 性能受阻在哪里?
(3)基数树
可以看得出来,锁竞争对于性能的消耗是很大的。
tcmalloc中 采用一种基数树的方式。从而避免锁带来的性能损耗
概述:
在计算机科学中,基数树,或称压缩前缀树,是一种更节省空间的Trie(前缀树)。对于基数树的每个节点,如果该节点是确定的子树的话,就和父节点合并。基数树可用来构建关联数组。 用于IP 路由。 信息检索中用于文本文档的倒排索引。
我们再来看看替换之后的效果吧
性能方面提升了很多了。
结语:
高并发内存池对数据结构、线程控制、设计模式有一定的要求。并且这种内存级别的项目,一旦出bug。找bug调试起来也很高。
对我自己而言,仅仅只是模仿了TCmalloc中的一些精华,还需要更加精炼自己对这个项目的理解。
ThreadCache:
class ThreadCache { public: void* Allocate(size_t size); void Delallocate(void* ptr,size_t size); void* FetchFromCentralCache(size_t index, size_t size); void ListTooLong(FreeList& list, size_t size); private: FreeList _freelist[NFREELIST]; }; static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr; void* ThreadCache::Allocate(size_t size) { assert(size < MAX_BYTES); size_t alignSize = SizeClass::RoundUP(size); size_t index = SizeClass::Index(size); if (!_freelist[index].Empty()) { return _freelist[index].Pop(); } return FetchFromCentralCache(index, alignSize); } void* ThreadCache::FetchFromCentralCache(size_t index, size_t size) { size_t batchNum = (std::min)(SizeClass::NumMoveSize(size),_freelist[index].MaxSize()); if (batchNum == _freelist[index].MaxSize()) { _freelist[index].MaxSize() += 1; } void* start = nullptr; void* end = nullptr; size_t acltualNum = CentralCache::GetInStance()->FetchRangeObj(start, end, batchNum, size); assert(acltualNum > 0); if (acltualNum == 1) { assert(start == end); } else { _freelist[index].PushRange(NextObj(start), end, acltualNum - 1); } return start; } void ThreadCache::Delallocate(void* ptr, size_t size) { assert(ptr); size_t index = SizeClass::Index(size); _freelist[index].Push(ptr); if (_freelist[index].MaxSize() < _freelist[index].Size()) { ListTooLong(_freelist[index],size); } } void ThreadCache::ListTooLong(FreeList& list, size_t size) { void* start = nullptr; void* end = nullptr; list.PopRange(start, end, list.MaxSize()); //...向CentralCache这一层归还span CentralCache::GetInStance()->ReleaseListToSpans(start, size); }
CentralCache:
class CentralCache { public: static CentralCache* GetInStance() { return &_Inst; } Span* GetOneSpan(SpanList& _list,size_t size); size_t FetchRangeObj(void*& start,void*& end,size_t batchNum,size_t size); void ReleaseListToSpans(void* start, size_t n); private: SpanList _spanlist[NFREELIST]; static CentralCache _Inst; CentralCache(){} CentralCache(const CentralCache&) = delete; }; CentralCache CentralCache::_Inst; Span* CentralCache::GetOneSpan(SpanList& _list, size_t size) { Span* it = _list.Begin(); while (it != _list.End()) { if (it->_freelist) { return it; } it = it->_next; } _list._mtx.unlock(); PageCache::GetInstance()->_pagemtx.lock(); Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size)); span->ObjectSize = size; span->_IsUse = true; PageCache::GetInstance()->_pagemtx.unlock(); char* start = (char*)(span->_pageId <<PAGE_SHIFT); size_t bytes = (span->n << PAGE_SHIFT); char* end = start + bytes; span->_freelist = start; start += size; void* tail = span->_freelist; while (start < end) { NextObj(tail) = start; //tail = start; tail = NextObj(tail); start += size; } NextObj(tail) = nullptr; _list._mtx.lock(); _list.PushFront(span); return span; } size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size) { size_t index = SizeClass::Index(size); _spanlist[index]._mtx.lock(); Span* span = GetOneSpan(_spanlist[index],size); assert(span); assert(span->_freelist); start = span->_freelist; end = start; size_t i = 0; size_t actulNum = 1; while (i < batchNum - 1 && NextObj(end)) { end = NextObj(end); i++; actulNum++; } span->_freelist = NextObj(end); NextObj(end) = nullptr; _spanlist[index]._mtx.unlock(); return actulNum; } void CentralCache::ReleaseListToSpans(void* start, size_t size) { size_t index = SizeClass::Index(size); _spanlist[index]._mtx.lock(); while (start) { void* next = NextObj(start); Span* span = PageCache::GetInstance()->MapObjectToSpan(start); NextObj(start) = span->_freelist; span->_freelist = start; span->_UseCount--; if (span->_UseCount == 0) { _spanlist[index].Erase(span); span->_freelist = nullptr; span->_next = nullptr; span->_prev = nullptr; _spanlist[index]._mtx.unlock(); //返还给下层PageCahce PageCache::GetInstance()->_pagemtx.lock(); PageCache::GetInstance()->ReleaseSpantoPageCache(span); PageCache::GetInstance()->_pagemtx.unlock(); } start = next; } _spanlist[index]._mtx.unlock(); }
PageCache:
class PageCache { public: static PageCache* GetInstance() { return &_Inst; } Span* NewSpan(size_t k); Span* MapObjectToSpan(void* obj); void ReleaseSpantoPageCache(Span* span); std::mutex _pagemtx; private: SpanList _spanlist[NPAGES]; static PageCache _Inst; ObjectPool<Span> _spanPool; TCMalloc_PageMap1<32 - PAGE_SHIFT> _idMapSpan; //std::unordered_map<PAGE_ID, Span*> _idMapSpan; PageCache() {} PageCache(const PageCache&) = delete; }; PageCache PageCache::_Inst; Span* PageCache::NewSpan(size_t k) { assert(k > 0); if (k > NPAGES - 1) { void* ptr = SystemAlloc(k); //Span* span = new Span; Span* span = _spanPool.New(); span->_pageId = ((PAGE_ID)ptr >> PAGE_SHIFT); span->n = k; //_idMapSpan[span->_pageId] = span; _idMapSpan.set(span->_pageId,span); return span; } else { if (!_spanlist[k].Empty()) { Span* kSpan = _spanlist[k].PopFront(); for (PAGE_ID i = 0;i < kSpan->n;++i) { /*_idMapSpan[kSpan->_pageId + i] = kSpan;*/ _idMapSpan.set(kSpan->_pageId+i, kSpan); } return kSpan; } for (size_t i = k + 1; i < NPAGES;++i) { if (!_spanlist[i].Empty()) { Span* nSpan = _spanlist[i].PopFront(); Span* kSpan = _spanPool.New(); //Span* kSpan = new Span; kSpan->_pageId = nSpan->_pageId; kSpan->n = k; nSpan->_pageId += k; nSpan->n -= k; _spanlist[nSpan->n].PushFront(nSpan); /* _idMapSpan[nSpan->_pageId] = nSpan; _idMapSpan[nSpan->_pageId + nSpan->n - 1] = nSpan;*/ _idMapSpan.set(nSpan->_pageId, nSpan); _idMapSpan.set(nSpan->_pageId + nSpan->n - 1, nSpan); for (PAGE_ID i = 0;i < kSpan->n;++i) { //_idMapSpan[kSpan->_pageId + i] = kSpan; _idMapSpan.set(kSpan->_pageId + i, kSpan); } return kSpan; } } //Span* bigSpan = new Span; Span* bigSpan = _spanPool.New(); void* ptr = SystemAlloc(NPAGES - 1); bigSpan->_pageId = ((PAGE_ID)ptr >> PAGE_SHIFT); bigSpan->n = NPAGES - 1; _spanlist[bigSpan->n].PushFront(bigSpan); return NewSpan(k); } } Span* PageCache::MapObjectToSpan(void* obj) { PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT; auto span = (Span*)_idMapSpan.get(id); assert(span); return span; //std::unique_lock<std::mutex>lock(_pagemtx); //auto ret = _idMapSpan.find(id); //if (ret != _idMapSpan.end()) //{ // return ret->second; //} //else //{ // assert(false); // return nullptr; //} } void PageCache::ReleaseSpantoPageCache(Span* span) { assert(span); if (span->n > NPAGES - 1) { void* ptr = (void*)(span->_pageId << PAGE_SHIFT); SystemFree(ptr); //delete span; _spanPool.Delete(span); return; } while (1) { PAGE_ID prevID = span->_pageId - 1; Span* prevSpan = (Span*)_idMapSpan.get(prevID); /* auto ret = _idMapSpan.find(prevID);*/ if (prevSpan == nullptr) { break; } if (prevSpan->n + span->n> NPAGES) { break; } if (prevSpan->_IsUse == true) { break; } span->_pageId = prevSpan->_pageId; span->n += prevSpan->n; _spanlist[prevSpan->n].Erase(prevSpan); //delete prevSpan; _spanPool.Delete(prevSpan); } while (1) { PAGE_ID nextId = span->_pageId + span->n; Span* nextSpan = (Span*)_idMapSpan.get(nextId); /*auto ret = _idMapSpan.find(nextId);*/ /* if (ret == _idMapSpan.end()) { break; }*/ if (nextSpan == nullptr) break; if (nextSpan->_IsUse == true) break; if (nextSpan->n + span->n > NPAGES) break; span->n += nextSpan->n; _spanlist[nextSpan->n].Erase(nextSpan); //delete nextSpan; _spanPool.Delete(nextSpan); } _spanlist[span->n].PushFront(span); span->_IsUse = false; //_idMapSpan[span->_pageId] = span; //_idMapSpan[span->_pageId + span->n - 1] = span; _idMapSpan.set(span->_pageId, span); _idMapSpan.set(span->_pageId+span->n - 1, span); }
本篇就到此结束了
感谢你的阅读!