当前位置: 首页 > news >正文

网站构建计划网站做链接

网站构建计划,网站做链接,无锡网站建设机构,wordpress审核插件目录 有关大于256KB内存的申请和释放处理方法 处理大于256KB的内存申请 补充内容1 补充内容2 补充内容3 处理大于256KB的内存释放 新增内容1 新增内容2 测试函数 使用定长内存池替代new 释放对象时不传对象大小 补充内容1 补充内容2 补充内容3 补充内容4 测试…

目录

有关大于256KB内存的申请和释放处理方法 

处理大于256KB的内存申请

补充内容1

补充内容2

补充内容3 

处理大于256KB的内存释放

新增内容1

新增内容2

测试函数

使用定长内存池替代new

释放对象时不传对象大小

补充内容1

补充内容2

 补充内容3

补充内容4

测试函数

为哈希桶加锁

多线程环境下对比malloc测试

复杂问题的调试技巧

性能瓶颈分析

针对性能瓶颈使用基数树进行优化

基数树代码


有关大于256KB内存的申请和释放处理方法 

内存申请和释放大于256KB也分为两种情况:

  1. 大于256KB但是小于128 * 8 * 1024KB
  2. 大于128 * 8 * 1024KB

内存申请大于256KB但小于128 * 8 * 1024KB:因为PageCache中存放的span所管理的最大页数为128,即span可分配的最大内存为128 * 8 * 1024 KB,所以当可以直接向PageCache申请一个合适的span(该span管理的页数为此次内存申请对齐后大小 / 页大小)

内存申请大于128 * 8 *1024 KB:PageCache中已经没有合适的span了,直接向堆上申请

处理大于256KB的内存申请

补充内容1

在RoundUp函数中新增用于处理大于256KB内存申请的内存对齐判断

(_RoundUp函数不变)

//用于内存对齐
static inline size_t RoundUp(size_t size)
{if (size <= 128){return _RoundUp(size, 8);}else if (size <= 1024){return _RoundUp(size, 16);}else if (size <= 8 * 1024){return _RoundUp(size, 128);}else if (size <= 64 * 1024){return _RoundUp(size, 1024);}else if (size <= 256 * 1024){return _RoundUp(size, 8 * 1024);}else{return _RoundUp(size, 1 << PAGE_SHIFT);//对齐数为当前规定的页的大小}
}

补充内容2

在NewSpan开始处中新增获取管理页数大于128的span的判断

//所需页数大于128PageCache无法分配,需要向堆申请
if (k > NPAGES - 1)
{void* ptr = SystemAlloc(k);//从堆获取一块内存Span* span = new Span;//申请一个span结点span->_PageId = (size_t)ptr >> PAGE_SHIFT;//新span中的页号为ptr指向的内存地址 / 页大小span->_n = k;//新span中的页数为n个_idSpanMap[span->_PageId] = span;//存放映射关系,即使该span不会被挂在PageCache上return span;
}

补充内容3 

在ConcurrentAlloc函数新增size > MAX_BYTES的判断

//申请内存
static void* ConcurrentAlloc(size_t size)
{if (size > MAX_BYTES){size_t alignSize = SizeClass::RoundUp(size);//内存对齐size_t kpage = alignSize >> PAGE_SHIFT;//计算所需页数PageCache::GetInstance()->_pageMtx.lock();Span* span = PageCache::GetInstance()->NewSpan(kpage);//向PageCache申请管理kpage个页的span,若kpage > 128则需要经过补充内容2中的内容,否则还是按照原NewSpan执行void* ptr = (void*)(span->_PageId << PAGE_SHIFT);//通过页号计算地址PageCache::GetInstance()->_pageMtx.unlock();return ptr;//返回从PageCache分配给的内存空间的地址}else{...//获取TLS的那部分内容}
}

处理大于256KB的内存释放

新增内容1

在ReleaseSpanToPageCache开始处新增当归还的span的_n > 128的判断

//大于128页的span直接还给堆
if (span->_n > NPAGES - 1)
{void* ptr = (void*)(span->_PageId << PAGE_SHIFT);SystemFree(ptr);delete span;return; 
}

新增内容2

在ConcurrentFree中新增用于size > MAX_BYTES的判断

//释放内存
static void ConcurrentFree(void* ptr,size_t size)
{if (size > MAX_BYTES){Span* span = PageCache::GetInstance()->MapObjectToSpan(ptr);//根据归还的内存地址获取要归还的spanPageCache::GetInstance()->_pageMtx.lock();PageCache::GetInstance()->ReleaseSpanToPageCache(span);//将要归还的span挂在PageCache上,或者返还给堆PageCache::GetInstance()->_pageMtx.unlock();}else{...//还是原来的那两行释放内容}
}

测试函数

//用于测试内存申请和释放大于256KB
void BigAlloc()
{//内存申请和释放大于256KB,但是小于128 * 8 * 1024KBvoid* p1 = ConcurrentAlloc(257 * 1024);ConcurrentFree(p1, 257 * 1024);//内存申请和释放大于128 * 8 * 1024KBvoid* p2 = ConcurrentAlloc(129 * 8 * 1024);ConcurrentFree(p2, 129 * 8 * 1024);
}

使用定长内存池替代new

基本概念:定长内存池在申请内存时是直接向堆申请的,没有使用malloc,效率得到提升,而目前我们在本项目中用到到了很多new的操作,其本质还是malloc,因此我们要用定长内存池中的New()和Delete()函数来代替new和delete,进行内存申请和释放,从而提高程序执行效率 

准备工作:在某个涉及new或者delete的类中新增ObjectPool< ?>类型的私有成员变量,下面以PageCache为例

class PageCache
{
public:...
private:...ObjectPool<Span> _spanPool;//<>中的类型根据需要进行更改...
};

替换方式:将PageCache.cpp中所有使用new的地方都换成_spanPool.New(),将所有使用delet的位置都换为_spanPool.Delete( ? )(?表示要删除的对象的名称可能是span也可能是kspan之类的)

//Span* kSpan = new Span;
Span* kSpan = _spanPool.New();//delete span;
_spanPool.Delete(span);

易忽略位置

1、ConcurrentAlloc.h中的new ThreadCache

//pTLSThreadCache = new ThreadCache;
static ObjectPool<ThreadCache> tcPool;//static修饰保证只在当前文件中可以被访问
pTLSThreadCache = tcPool.New();

!!!重点!!! 

注意事项:pTLSThreadCache是每个线程独有的一个对象,但是为其申请空间的tcPool不是,它是一个静态的对象,整个进程中独一份,被当前进程中的所有线程共享,多线程情况下会出现线程安全问题,所以这里也需要加锁(不加的话有小概率不崩溃,即轮到t2执行时_memory不为空)

解决办法:在ObjPool类中新增公有成员变量_poolMtx,同时在pTLSThreadCache = tcPool.New()的两侧加锁

tcPool._poolMtx.lock();
pTLSThreadCache = tcPool.New();
tcPool._poolMtx.unlock();

补充:SpanList类中为了创建头结点的new Span不用替换,因为头节点通常在 SpanList 对象的整个生命周期内存在,并且不会像其他 Span 对象那样频繁创建和销毁。使用 _spanPool 进行内存管理主要是为了优化频繁分配和回收的对象,而头节点的长生命周期使得使用 _spanPool 的优势不明显

释放对象时不传对象大小

基本概念:在之前释放内存时我们不仅要传入释放的内存的地址,还要存放要释放的内存的大小,过于麻烦,所以最好只传递一个指针即可释放内存

ConcurrentFree(p1, 6);

补充内容1

在span类中新增一个表示当前span中管理的内存的大小的成员变量_objSize

struct Span
{...size_t _objSize = 0;//当前span管理的内存大小
};

补充内容2

在ConcurrentFree中,将MapObjectToSpan的位置进行移动,并获取当前span的_objSize

//释放内存
static void ConcurrentFree(void* ptr)
{Span* span = PageCache::GetInstance()->MapObjectToSpan(ptr);size_t size = span->_objSize;if (size > MAX_BYTES){...}else{...}
}

 补充内容3

在ConcurrentAlloc中从堆上获取到一个span后,补充该span的_objSize

//申请内存
static void* ConcurrentAlloc(size_t size)
{if (size > MAX_BYTES){...Span* span = PageCache::GetInstance()->NewSpan(kpage);span->_objSize = size;...}else{...}
}

补充内容4

CentralCache中的NewSpan后,填充该span的_objSize

//为指定位置桶下的SpanList申请一个非空的span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{...Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));//从PageCache中获取一个新的非空spanspan->_isUse = true;span->_objSize = size;....
}

 注意事项:记得最后把ConcurrentFree的第二个形参删除

测试函数

void WithNoSize()
{void* p1 = ConcurrentAlloc(257 * 1024);ConcurrentFree(p1);void* p2 = ConcurrentAlloc(129 * 8 * 1024);ConcurrentFree(p2);
}

为哈希桶加锁

基本概念:C++的标准模板库(STL)提供的容器在多线程环境下并不保证线程安全,因此在多个线程同时访问或修改同一个容器时,通常需要自行添加同步机制(如互斥锁)以确保数据的一致性和避免竞态条件,因此当我们尝试在本项目中使用哈希桶记录span与页号的映射关系时,需要及时的加锁

加锁位置:参与读写哈希桶的函数有NewSpan、MapObjectToSpan和ReleaseSpanToPageCache,它们都在PageCache.cpp中,其中在CentralCache.cpp中使用这三个函数时,只有MapObjectToSpan没有添加锁,这就可能导致多个线程在CentralCache中同时访问MapObjectToSpan函数并同时访问哈希桶造成线程安全问题,所以要在MapObjectToSpan函数执行到访问哈希桶的操作前加锁

//地址->页号->span的映射
Span* PageCache::MapObjectToSpan(void* obj)
{size_t id = ((size_t)obj >> PAGE_SHIFT);std::unique_lock<std::mutex> lock(_pageMtx);auto ret = _idSpanMap.find(id);if (ret != _idSpanMap.end()){return ret->second;}else{assert(false);return nullptr;}
}

关于std::unique_lock<std::mutex> lock(_pageMtx):是一种通过RAII方式管理互斥锁的机制,确保在多线程环境中对共享资源的安全访问。它自动处理锁的获取和释放,减少了手动管理锁可能带来的错误风险,同时提供了较高的灵活性,适用于各种复杂的同步场景

多线程环境下对比malloc测试

新增Benchmark.cpp源文件

#include<cstdio>
#include<iostream>
#include<vector>
#include<thread>
#include<mutex>
#include"ConcurrentAlloc.h"
using namespace std;void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds)//ntime一轮申请和释放内存的次数,round是跑多少轮,nworks是线程数
{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();}printf("%u个线程并发执行%u轮次,每轮次malloc %u次: 花费:%u ms\n",nworks, rounds, ntimes, malloc_costtime.load());printf("%u个线程并发执行%u轮次,每轮次free %u次: 花费:%u ms\n",nworks, rounds, ntimes, free_costtime.load());printf("%u个线程并发malloc&free %u次,总计花费:%u ms\n",nworks, nworks * rounds * ntimes, malloc_costtime.load() + free_costtime.load());
}// 单轮次申请释放次数 线程数 轮次
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();}printf("%u个线程并发执行%u轮次,每轮次concurrent alloc %u次: 花费:%u ms\n",nworks, rounds, ntimes, malloc_costtime.load());printf("%u个线程并发执行%u轮次,每轮次concurrent dealloc %u次: 花费:%u ms\n",nworks, rounds, ntimes, free_costtime.load());printf("%u个线程并发concurrent alloc&dealloc %u次,总计花费:%u ms\n",nworks, nworks * rounds * ntimes, malloc_costtime.load() + free_costtime.load());
}
int main()
{size_t n = 10000;cout << "==========================================================" << endl;BenchmarkConcurrentMalloc(n, 10, 10);cout << endl << endl;BenchmarkMalloc(n, 10, 10);cout << "==========================================================" << endl;return 0;
}

性能瓶颈分析

针对性能瓶颈使用基数树进行优化

基数树代码

 ~over~

http://www.yayakq.cn/news/387623/

相关文章:

  • 农业科技公司网站案例网站集群建设必要性
  • 张家港外贸网站制作义乌外发联合加工网
  • 想做网站找什么公司设计师免费资源导航
  • 郑州做网站设计一键生成房屋设计图
  • 深圳专业的网站制作公司河南省建筑市场一体化平台
  • 表白网站制作平台站长之家
  • 美工个人网站有一个网站自己做链接获取朋友位置
  • 手机公众平台网站开发数码产品网站模板
  • 有哪些网站可以免费的宁波城乡住房建设厅网站
  • 网站建设公司968做网站最主要是那个一类商标
  • html5做的网站安全之要
  • 网站开发之美美食网站建设项目预算
  • 黄州区精神文明建设网站中国房地产网站
  • 简历旅游网站开发经验旅游网站建设的可行性分析
  • 群晖 nas做网站 推荐玉环网站制作
  • 河南做网站公司排名小程序开发者
  • 资讯是做网站还是公众号网站开发主要语言
  • 湖北做网站平台哪家好做教育机构网站
  • 模块化wordpress企业主题seo刷关键词排名软件
  • 安徽亳州建设厅网站吉林移动网站
  • 如何更新单位网站网站首页欣赏
  • 国外网站怎么进入南阳网站搭建
  • 南宁网站建设及推广免费ps素材图片大全
  • 在线网站设计工具网站开发 哪些文档
  • 如何网站开发语言500云空间网站
  • 营销型企业网站建设体会wordpress心理教育网站
  • 明星粉丝网站怎么做的百度商城官网首页
  • 深圳建设工程信息网站做免费看电影的网站不违法吗
  • 做淘宝客网站需要工商营业执照只有一个人网站开发
  • 品牌网站设计哪家好免费ui设计网站