C++项目——高并发内存池(7)--基数树对于访问临界资源_idSpanMap的优化

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 每个桶都有申请的情景

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值