1. 性能分析
1.1 优化思路
综上,还是频繁的申请锁导致程序很慢。
有没有一种方法可以,避免在访问_idSpanMap时加锁呢?首先先思考为什么在用哈希结构式时读写_idSpanMap需要加锁呢?
如果一个线程正在从_idSpanMap中读取Span,在这个过程中,它势必已经进行了一部分的搜索,但是如果我此时又向其中插入了一个映射关系,势必会对该结构产生影响。那我读操作,也必定会受到影响,所以不能同时读写是_idSpanMap内部需要单独加锁的原因。所以需要将哈希结构进行改变
2. 基数树
类似使用定长数组的哈希表,创建时就确定长度,数组每个值存的都是一个指针。创建该类的对象时,对于_idSpanMap的写操作就已经完成,且不会增删改,所以这种哈希结构可以支持一边读一边写。
#pragma once
#include"Common.h"
//使用方法
//TCMalloc_PageMap1<32 - PAGE_SHIFT> _idSpanMap;
// 32-PAGE_SHIFT 意思就是 2^32 / 2^13 总内存除以 8K
// 得到的是有多少个8k 即有多少个页号
//传入的模板参数的意思是 需要用多少位来保存页号
// Single-level array
template <int BITS> //BITS=19
class TCMalloc_PageMap1 {
private:
//需要多少位来保存 页号 这里是 32-13=19 需要19位来保存页号
static const int LENGTH = 1 << BITS;
void** array_;
public:
typedef uintptr_t Number;
explicit TCMalloc_PageMap1()
{
//因为有2^19次方个 每个大小为指针大小 算的是总大小
size_t size = sizeof(void*) << BITS;
//按页对齐
size_t alignSize = SizeClass::_RoundUp(size, 1 << PAGE_SHIFT);
//向堆按页申请空间 array_为起始地址
array_ = (void**)SystemAlloc(alignSize >> PAGE_SHIFT);
memset(array_, 0, sizeof(void*) << BITS);
}
//拿对应的值
void* get(Number k) const
{
if ((k >> BITS) > 0)
{
return NULL;
}
return array_[k];
}
//建立映射关系
void set(Number k, void* v)
{
array_[k] = v;
}
};
3. 修改相关代码
class PageCache
{
public:
std::mutex _pageMtx;
static PageCache* GetInstance()
{
return &_sInst;
}
Span* NewSpan(size_t k);
Span* MapObjectToSpan(void* obj);
void ReleaseSpanToPageCache(Span* span);
private:
PageCache()
{}
PageCache(const PageCache&) = delete;
static PageCache _sInst;
SpanList _spanLists[NPAGES];
//std::unordered_map<PAGE_ID, Span*> _idSpanMap;
TCMalloc_PageMap1<32 - PAGE_SHIFT> _idSpanMap;
ObjectPool<Span> _spanPool;
};
4. 测试结果
测试代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"ConcurrentAlloc.h"
// ntimes 一轮申请和释放内存的次数
// rounds 轮次
void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds)
{
std::vector<std::thread> vthread(nworks);
std::atomic<size_t> malloc_costtime = 0;
std::atomic<size_t> free_costtime = 0;
for (size_t k = 0; k < nworks; ++k)
{
vthread[k] = std::thread([&, k]() {
std::vector<void*> v;
v.reserve(ntimes);
for (size_t j = 0; j < rounds; ++j)
{
size_t begin1 = clock();
for (size_t i = 0; i < ntimes; i++)
{
v.push_back(malloc(16));
//v.push_back(malloc((16 + i) % 8192 + 1));
}
size_t end1 = clock();
size_t begin2 = clock();
for (size_t i = 0; i < ntimes; i++)
{
free(v[i]);
}
size_t end2 = clock();
v.clear();
malloc_costtime += (end1 - begin1);
free_costtime += (end2 - begin2);
}
});
}
for (auto& t : vthread)
{
t.join();
}
cout << nworks << "个线程并发执行" << rounds << "轮次,每轮次malloc "
<< ntimes << "次: 花费:" << malloc_costtime << "ms" << endl;
cout << nworks << "个线程并发执行" << rounds << "轮次,每轮次free "
<< ntimes << "次: 花费:" << free_costtime << "ms" << endl;
cout << nworks << "个线程并发执行" << rounds <<"malloc&free "
<< nworks * rounds * ntimes << "次,总计花费:" << malloc_costtime + free_costtime << "ms" << endl;
}
// 单轮次申请释放次数 线程数 轮次
void BenchmarkConcurrentMalloc(size_t ntimes, size_t nworks, size_t rounds)
{
std::vector<std::thread> vthread(nworks);
std::atomic<size_t> malloc_costtime = 0;
std::atomic<size_t> free_costtime = 0;
for (size_t k = 0; k < nworks; ++k)
{
vthread[k] = std::thread([&]() {
std::vector<void*> v;
v.reserve(ntimes);
for (size_t j = 0; j < rounds; ++j)
{
size_t begin1 = clock();
for (size_t i = 0; i < ntimes; i++)
{
v.push_back(ConcurrentAlloc(16));
//v.push_back(ConcurrentAlloc((16 + i) % 8192 + 1));
}
size_t end1 = clock();
size_t begin2 = clock();
for (size_t i = 0; i < ntimes; i++)
{
ConcurrentFree(v[i]);
}
size_t end2 = clock();
v.clear();
malloc_costtime += (end1 - begin1);
free_costtime += (end2 - begin2);
}
});
}
for (auto& t : vthread)
{
t.join();
}
cout << nworks << "个线程并发执行" << rounds << "轮次,每轮次concurrent alloc "
<< ntimes << "次: 花费:" << malloc_costtime << "ms" << endl;
cout << nworks << "个线程并发执行" << rounds << "轮次,每轮次concurrent dealloc "
<< ntimes << "次: 花费:" << free_costtime << "ms" << endl;
cout << nworks << "个线程并发执行" << rounds << "concurrent alloc&dealloc "
<< nworks * rounds * ntimes << "次,总计花费:" << malloc_costtime + free_costtime << "ms" << endl;
}
int main()
{
size_t n = 10000;
cout << "==========================================================" << endl;
BenchmarkConcurrentMalloc(n, 4, 10);
cout << endl << endl;
BenchmarkMalloc(n, 4, 10);
cout << "==========================================================" << endl;
return 0;
}
4.1 每次都申请一个桶的场景
4.2 每个桶都有申请的情景