原本,当线程资源层ThreadCache向中心资源层CentreCache申请资源时,中心资源层会在返回给线程资源层的内存的函数GetRangeMemory中加上桶锁,在GetRangeMemory内可能调用GetOneSpan向页缓存资源层申请资源,此时又会加上页缓存资源层的全锁。
现在,进行优化:
- 优化原因:当CentreCache调用GetOneSpan向页缓存资源层申请资源时,CentreCache的桶锁未解开,这样其他线程闲置的资源就放不会这个桶里了,影响效率
- 优化步骤:当CentreCache调用GetOneSpan向页缓存资源层申请资源时,解开桶锁,当CentreCache成功向页缓存资源层申请资源后,重新申请桶锁,再将申请来的多余资源插入进桶中
size_t CentreCache::GetRangeMemory(void*& start, void*& end, size_t n, size_t index,size_t alignedSize)//start、end为输出型参数,返回span内存包中分配出去的部分内存块链的起始和尾部,n代表要分配的内存块数量,index代表spanLists链的桶编号(与ThreadCache层的桶的编号是对应的),alignedSize为桶内所装内存块的大小,返回值为实际分配出去的内存块数量
{
spanLists[index].Getmutex().lock();//加上桶锁
Span* sp = GetOneSpan(&spanLists[index], index,alignedSize);
assert(sp);
assert(sp->freelist != nullptr);
size_t num = n-1;//end要下走的步数
start = sp->freelist;
end = start;
while (num > 0 && NextNode(end) != nullptr)
{
--num;
end = NextNode(end);
}
sp->freelist = NextNode(end);
sp->usedBlocks += n - num;//n-num即为实际分配出去的内存块数量
spanLists[index].Getmutex().unlock();
return n - num;//n-num即为actualNumber
}
//GetOneSpan涉及中心资源层与页缓存资源层的交互,是线程竞争最激烈的地方,锁设计的必须精巧
Span* CentreCache::GetOneSpan(SpanList* sl, size_t index, size_t alignedSize)//从中心资源层找到一个符合线程资源层所申请的Span包,如没有,向页缓存资源层申请一个新的Span块
{
Span* oneSpan = spanLists[index].GetNoEmptySpan();
if (oneSpan != nullptr)
return oneSpan;
spanLists[index].Getmutex().unlock();//向页缓存资源层申请新Span块的时候解开桶锁,以免耽误其他线程归还该桶里的Span块,这样可能会导致一个线程刚申请完Span还没来得及将Span块插入中心资源处桶里时,其他线程就又来申请新的Span块了,但无伤大雅,不处理这种情况更佳,省了下次再申请,反正多余的Span块也会被适时回收的
PageCache::GetPageCache().GetPageCacheMUTEX().lock();//向页缓存资源层申请资源必须加全锁
PageCache& pageCache = PageCache::GetPageCache();
oneSpan = pageCache.NewSpan(SizeClass::PageCountCal(alignedSize));
PageCache::GetPageCache().GetPageCacheMUTEX().unlock();
//先假设,Span得页号是整个用户地址空间内的页号,并且它一定处于PageCache内申请来得内存块内,例通过(p+8K)/8K得来得页号
//再假设,PageCache返回得Span块内只写好了PAGE_ID,而没有绑定好freelists
char* start = (char*)(oneSpan->pageID << PAGE_SHIFT);
char* end = start +(oneSpan->counts << PAGE_SHIFT);
//将新申请来的Span块切割成每个大小为alignedSize的内存块
char* cur = start;
char* next = start + alignedSize;
while (next < end)
{
NextNode(cur) = next;
cur = next;
next = next + alignedSize;
}
NextNode(cur) = nullptr;
oneSpan->freelist = (void*)(oneSpan->pageID << PAGE_SHIFT);
//将新申请来的Span块插入到中心资源层对应SpanLists链中去
spanLists[index].Getmutex().lock();//此处需要改变中心资源层桶链的结构,需要申请回桶锁.
sl->PushFront(oneSpan);
return oneSpan;
}