为了使多个线程并发地分配和释放内存,必须在分配器方法中添加互斥锁。

全局内存管理器(通过new()和delete()实现)是通用的,因此它的开销也非常大。

因为单线程内存管理器要比多线程内存管理器快的多,所以如果要分配的大多数内存块限于单线程中使用,那么可以显著提升性能。

如果开发了一套有效的单线程分配器,那么通过模板可以方便地将它们扩展到多线程环境中。

以下是测试代码(multi_threaded_memory_pool.cpp):

#include "multi_threaded_memory_pool.hpp"
#include <iostream>
#include <chrono>
#include <string>
#include <mutex>namespace multi_threaded_memory_pool_ {// reference: 《提高C++性能的编程技术》:第七章:多线程内存池class Rational1 {
public:Rational1(int a = 0, int b = 1) : n(a), d(b) {}
private:int n; // 分子int d; // 分母
};// 单线程可变大小内存管理器:
// MemoryChunk类取代之前版本中使用的NextOnFreeList类,它用来把不同大小的内存块连接起来形成块序列
class MemoryChunk {
public:MemoryChunk(MemoryChunk* nextChunk, size_t chunkSize);// 析构函数释放构造函数获得的内存空间~MemoryChunk() { delete [] mem; }inline void* alloc(size_t size);inline void free(void* someElement);// 指向列表下一内存块的指针MemoryChunk* nextMemChunk() { return next; }// 当前内存块剩余空间大小size_t spaceAvailable() { return chunkSize - bytesAlreadyAllocated; }// 这是一个内存块的默认大小enum { DEFAULT_CHUNK_SIZE = 4096 };private:MemoryChunk* next;void* mem;// 一个内存块的默认大小size_t chunkSize;// 当前内存块中已分配的字节数size_t bytesAlreadyAllocated;
};// 构造函数首先确定内存块的适当大小,然后根据这个大小从堆上分配私有存储空间
// MemoryChunk将next成员指向输入参数nextChunk, nextChunk是列表先前的头部
MemoryChunk::MemoryChunk(MemoryChunk* nextChunk, size_t reqSize)
{chunkSize = (reqSize > DEFAULT_CHUNK_SIZE) ? reqSize : DEFAULT_CHUNK_SIZE;next = nextChunk;bytesAlreadyAllocated = 0;mem = new char[chunkSize];
}// alloc函数处理内存分配请求,它返回一个指针,该指针指向mem所指向的MemoryChunk私有存储空间中的可用空间。
// 该函数通过更新该块中已分配的字节数来记录可用空间的大小
void* MemoryChunk::alloc(size_t requestSize)
{void* addr = static_cast<void*>(static_cast<char*>(mem) + bytesAlreadyAllocated);bytesAlreadyAllocated += requestSize;return addr;
}// 在该实现中,不用担心空闲内存段的释放。当对象被删除后,整个内存块将被释放并且返回到堆上
inline void MemoryChunk::free(void* doomed)
{
}// MemoryChunk只是一个辅助类,ByteMemoryPoll类用它来实现可变大小的内存管理
class ByteMemoryPool {
public:ByteMemoryPool(size_t initSize = MemoryChunk::DEFAULT_CHUNK_SIZE);~ByteMemoryPool();// 从私有内存池分配内存inline  void* alloc(size_t size);// 释放先前从内存池中分配的内存inline void free(void* someElement);private:// 内存块列表,它是我们的私有存储空间MemoryChunk* listOfMemoryChunks = nullptr;// 向我们的私有存储空间添加一个内存块void expandStorage(size_t reqSize);
};// 虽然内存块列表可能包含多个块,但只有第一块拥有可用于分配的内存。其它块表示已分配的内存。
// 列表的首个元素是唯一能够分配可以内存的块。// 构造函数接收initSize参数来设定一个内存块的大小,即构造函数借此来设置单个内存块的大小。
// expandStorage方法使listOfMemoryChunks指向一个已分配的MemoryChunk对象
// 创建ByteMemoryPool对象,生成私有存储空间
ByteMemoryPool::ByteMemoryPool(size_t initSize)
{expandStorage(initSize);
}// 析构函数遍历内存块列表并且删除它们
ByteMemoryPool::~ByteMemoryPool()
{MemoryChunk* memChunk = listOfMemoryChunks;while (memChunk) {listOfMemoryChunks = memChunk->nextMemChunk();delete memChunk;memChunk = listOfMemoryChunks;}
}// alloc函数确保有足够的可用空间,而把分配任务托付给列表头的MemoryChunk
void* ByteMemoryPool::alloc(size_t requestSize)
{size_t space = listOfMemoryChunks->spaceAvailable();if (space < requestSize) {expandStorage(requestSize);}return listOfMemoryChunks->alloc(requestSize);
}// 释放之前分配的内存的任务被委派给列表头部的MemoryChunk来完成
// MemoryChunk::free不做任何事情,因为ByteMemoryPool的实现不会重用之前分配的内存。如果需要更多内存,
// 我们将创建新的内存块以便今后分配使用。在内存池被销毁时,内存释放回堆中。ByteMemoryPool析构函数
// 释放所有的内存块到堆中
inline void ByteMemoryPool::free(void* doomed)
{listOfMemoryChunks->free(doomed);
}// 若遇到内存块用尽这种不太可能的情况,我们通过创建新的内存块并把它添加到内存块列表的头部来扩展它
void ByteMemoryPool::expandStorage(size_t reqSize)
{listOfMemoryChunks = new MemoryChunk(listOfMemoryChunks, reqSize);
}// 多线程内存池实现
template<class POOLTYPE, class LOCK>
class MTMemoryPool {
public:// 从freeList里分配一个元素inline void* alloc(size_t size);// 返回一个元素给freeListinline void free(void* someElement);private:POOLTYPE stPool; // 单线程池LOCK theLock;
};// alloc方法将分配任务委托给内存池成员,而将锁定任务委托给锁成员
template<class M, class L>
inline void* MTMemoryPool<M, L>::alloc(size_t size)
{void* mem;theLock.lock();mem = stPool.alloc(size);theLock.unlock();return mem;
}template<class M, class L>
inline void MTMemoryPool<M, L>::free(void* doomed)
{theLock.lock();stPool.free(doomed);theLock.unlock();
}class ABCLock { // 抽象基类
public:virtual ~ABCLock() {}virtual void lock() = 0;virtual void unlock() = 0;
};class MutexLock : public ABCLock {
public:MutexLock() {}~MutexLock() {}inline void lock() { mtx.lock(); }inline void unlock() { mtx.unlock(); }private:std::mutex mtx;
};class Rational2 {
public:Rational2(int a = 0, int b = 1) : n(a),d(b) {}void* operator new(size_t size) { return memPool->alloc(size); }void operator delete(void* doomed, size_t size) { memPool->free(doomed); }static void newMemPool() { memPool = new MTMemoryPool<ByteMemoryPool, MutexLock>; }static void deleteMemPool() { delete memPool; }private:int n; // 分子int d; // 分母static MTMemoryPool<ByteMemoryPool, MutexLock>* memPool;
};MTMemoryPool<ByteMemoryPool, MutexLock>* Rational2::memPool = nullptr;///
// 多线程内存池实现应用在单线程环境中
class DummyLock : public ABCLock {
public:inline void lock() {}inline void unlock() {}
};class Rational3 {
public:Rational3(int a = 0, int b = 1) : n(a),d(b) {}void* operator new(size_t size) { return memPool->alloc(size); }void operator delete(void* doomed, size_t size) { memPool->free(doomed); }static void newMemPool() { memPool = new MTMemoryPool<ByteMemoryPool, DummyLock>; }static void deleteMemPool() { delete memPool; }private:int n; // 分子int d; // 分母static MTMemoryPool<ByteMemoryPool, DummyLock>* memPool;
};MTMemoryPool<ByteMemoryPool, DummyLock>* Rational3::memPool = nullptr;int test_multi_threaded_memory_pool_1()
{using namespace std::chrono;high_resolution_clock::time_point time_start, time_end;const int cycle_number1{10000}, cycle_number2{1000};{ // 测试全局函数new()和delete()的基准性能Rational1* array[cycle_number2];time_start = high_resolution_clock::now();for (int j =0; j < cycle_number1; ++j) {for (int i =0; i < cycle_number2; ++i) {array[i] = new Rational1(i);}for (int i = 0; i < cycle_number2; ++i) {delete array[i];} }time_end = high_resolution_clock::now();fprintf(stdout, "global function new/delete time spent: %f seconds\n",(duration_cast<duration<double>>(time_end - time_start)).count());
}{ // 多线程内存池测试代码Rational2* array[cycle_number2];time_start = high_resolution_clock::now();Rational2::newMemPool();for (int j = 0; j < cycle_number1; ++j) {for (int i = 0; i < cycle_number2; ++i) {array[i] = new Rational2(i);}for (int i = 0; i < cycle_number2; ++i) {delete array[i];}}Rational2::deleteMemPool();time_end = high_resolution_clock::now();fprintf(stdout, "multi-threaded variable-size memory manager time spent: %f seconds\n",(duration_cast<duration<double>>(time_end - time_start)).count());
}{ // 多线程内存池应用在单线程环境下测试代码Rational3* array[cycle_number2];time_start = high_resolution_clock::now();Rational3::newMemPool();for (int j = 0; j < cycle_number1; ++j) {for (int i = 0; i < cycle_number2; ++i) {array[i] = new Rational3(i);}for (int i = 0; i < cycle_number2; ++i) {delete array[i];}}Rational3::deleteMemPool();time_end = high_resolution_clock::now();fprintf(stdout, "multi-threaded variable-size memory manager in single-threaded environment time spent: %f seconds\n",(duration_cast<duration<double>>(time_end - time_start)).count());
}return 0;
}} // namespace multi_threaded_memory_pool_

执行结果如下:

GitHub:https://github.com/fengbingchun/Messy_Test

提高C++性能的编程技术笔记:多线程内存池+测试代码相关推荐

  1. 提高C++性能的编程技术笔记:引用计数+测试代码

    引用计数(reference counting):基本思想是将销毁对象的职责从客户端代码转移到对象本身.对象跟踪记录自身当前被引用的数目,在引用计数达到零时自行销毁.换句话说,对象不再被使用时自行销毁 ...

  2. 提高C++性能的编程技术笔记:编码优化+测试代码

    缓存:在现代处理器中,缓存经常与处理器中的数据缓存和指令缓存联系在一起.缓存主要用来存储使用频繁而且代价高昂的计算结果,这样就可以避免对这些结果的重复计算.如,循环内对常量表达式求值是一种常见的低性能 ...

  3. 提高C++性能的编程技术笔记:内联+测试代码

    内联类似于宏,在调用方法内部展开被调用方法,以此来代替方法的调用.一般来说表达内联意图的方式有两种:一种是在定义方法时添加内联保留字的前缀:另一种是在类的头部声明中定义方法. 虽然内联方法的调用方式和 ...

  4. 提高C++性能的编程技术笔记:临时对象+测试代码

    类型不匹配:一般情况是指当需要X类型的对象时提供的却是其它类型的对象.编译器需要以某种方式将提供的类型转换成要求的X类型.这一过程可能会产生临时对象. 按值传递:创建和销毁临时对象的代价是比较高的.倘 ...

  5. 提高C++性能的编程技术笔记:跟踪实例+测试代码

    当提高性能时,我们必须记住以下几点: (1). 内存不是无限大的.虚拟内存系统使得内存看起来是无限的,而事实上并非如此. (2). 内存访问开销不是均衡的.对缓存.主内存和磁盘的访问开销不在同一个数量 ...

  6. Multi-thread--提高C++性能的编程技术笔记:多线程内存池+测试代码

    为了使多个线程并发地分配和释放内存,必须在分配器方法中添加互斥锁. 全局内存管理器(通过new()和delete()实现)是通用的,因此它的开销也非常大. 因为单线程内存管理器要比多线程内存管理器快的 ...

  7. 提高C++性能的编程技术笔记:总结

    <提高C++性能的编程技术>这本书是2011年出版的,书中有些内容的介绍可能已经过时,已不再适用于现在的C++编程中,但大部分内容还是很有参考意义的. 这里是基于之前所有笔记的简单总结,笔 ...

  8. 提高C++性能的编程技术笔记:设计优化/可扩展性/系统体系结构相关+测试代码

    1. 设计优化 我们可以粗略地将性能优化分为两种类型:编码优化和设计优化.编码优化定义为不需要完整理解要解决的问题或者应用程序的执行流程就能实施的优化.通过定义看出,编码优化用于局部代码,同时该过程不 ...

  9. 提高C++性能的编程技术笔记:单线程内存池+测试代码

    频繁地分配和回收内存会严重地降低程序的性能.性能降低的原因在于默认的内存管理是通用的.应用程序可能会以某种特定的方式使用内存,并且为不需要的功能付出性能上的代价.通过开发专用的内存管理器可以解决这个问 ...

最新文章

  1. DeepLearning 应用概述
  2. 用专业的说一句情话 计算机,用电脑专业说一句情话
  3. css中调整高度充满_CSS(十三).高度如何铺满全屏
  4. android之数据存储,Android数据存储之File
  5. 表格单元格中的CSS文本溢出?
  6. 标志位鼠标Java_检查标志位java
  7. SQL Server 2008之DMF
  8. 仍然报错_only_full_group_by配置,竟让所有应用报错?
  9. android 函数式编程_Android开发人员的函数式编程-第1部分
  10. VSCode工具常用命令总结
  11. 计算机软件开发即征即退,自行开发软件产品增值税实行即征即退政策
  12. MacOS 10.15编译openjdk8u详细过程
  13. amr文件怎么转换成mp3
  14. Unreal Engine 4(虚幻UE4)GameplayAbilities 插件入门教程(三)技能标签(Ability Tags)
  15. 市场营销环境分析的方法
  16. 机器人教育对孩子们的作用
  17. Db2 SQL PL简介
  18. 在Linux中安装mysql后遇到错误20008解决方案
  19. python莫比乌斯内接矩形_用莫比乌斯带巧解内接矩形问题:拓扑学的用处
  20. 阿里巴巴微服务架构演进

热门文章

  1. 概率论—随机变量的数字特征、大数定律及中心极限定理
  2. 三维点云课程第一章:应用
  3. 1.Socket通信
  4. python图像中如何显示中文
  5. OpenCV 遇到的问题
  6. android 轮播 中间变大,如何实现中间大两头小的轮播效果
  7. 【开源方案共享】无序点云快速的线段分割算法
  8. 【非专业评测】发蜡、发膏、发泥、发油、啫喱、发膜、发胶、干胶、发棒、摩丝
  9. 安装Nginx的方法教程
  10. 在CentOS 6.9 x86_64的nginx 1.12.2上开启echo-nginx-module模块实录