前言
笔者在写libuv的时候遇到太多uv_type_t和buffer需要维护了.
如果不通过内存池维护,一个是new delete效率非常低,另一个是这种callback和作用域所有权互相耦合的场景,即便是很好的维护了new delete,也会使得libuv运行的环境变得非常混乱.
所以笔者决定去github上考察一下c++内存池,目前已知的几个如下:
star最多的,最简洁的,可以很方便的嵌入到程序里,只包含一个.h和一个.tcc文件,完全可以自己把他们揉在一起.
跨平台
采用MIT协议
.
不再维护
非线程安全
可以构造参数传递
创建用时大概是new的50%,delete就要节约更多啦
包含c++98和c++11两个版本,和c++版本强耦合
的,都包含了一些在后面被decrepit的写法所以版本不一致时可能需要自己改一点点.
c++11的版本中支持了std::forword等,支持使用对象的构造方法,支持绕过构造方法(malloc形式).
支持与std::allocate语法兼容,比如可以直接这样使用:
std::vector< int, MemoryPool< int > > v;
默认池大小为4096可以在源码里调整.通过成员函数申请失败时由返回值表征.
通过std::allocate语法兼容申请时,在超过可用空间后只会直接报错.
不要试图很紧巴的使用这个内存池,且不要试图通过blockSize估计可以放置的元素数量,因为它代码里实际的maxSize在计算时如果blockSize较大是会有一些误差的.
推荐场景
小项目且内存比较充裕,直接给个很大的BlockSize怎么用都没事.如果你想这里不出问题,BlockSize需要大于这个池子可能容纳的对象的所有字节数的上界.
线程安全
无锁
和cacay/MemoryPool接口基本一致,接口设计还是不错的.
这个作者本来想把上面那个拿来开发一个线程安全的版本,但是他发现上面那个在改线程安全时各种奇怪错误太多了.上面那个写的确实不美.
然后他基本推到从写了一个线程安全的.效率比起来当然低很多.
面向数据
跨平台
写作方法也比较现代
,也很精巧,只需要吧.h和.cpp复制进去就行,还有自己的命名空间
个人最推荐的库.
非线程安全
详细的文档和benchmark
可以构造参数传递
创建用时大概是new的3/4,delete就要节约更多啦
样例丰富
性能在win下比较好,因为win下new和delete成本比较高,linux下性能一般.
是APPShift库的一部分,另外,在我写完这篇的时候点进官网看了一下,进去直接是希伯来语给我整傻了,希伯来语看上去真奇怪啊.
推荐场景
项目比较大比较复杂的时候,这个库的接口设计个人觉得不如最上面那个,但是也没有一些奇怪的写法.不会导致突然的error.同时维护比较积极,后面还会添加无锁的版本.
测试代码
#include "MemoryPool.h"
#include <cstdint>
#include <ctime>
#include <iostream>
#include <random>
#include <string>
#include <vector>
class A
{
public:
// A()
// {
// std::random_device rd;
// a = rd();
// }
// A(){};
// A( int a, int b, int c ) {}
int a;
std::string b;
};
constexpr int REPETTIMES = 10000000;
int main( int argc, char** argv )
{
decltype( clock() ) timeBegin;
decltype( clock() ) timeEnd;
{
timeBegin = clock();
std::vector< A > as;
for ( int i = 0; i < REPETTIMES; ++i ) {
as.emplace_back( *new A );
}
timeEnd = clock();
std::cout << "use emplace_back&&new cost=" << ( timeEnd - timeBegin ) / ( CLOCKS_PER_SEC / 1000 ) << std::endl;
}
{
timeBegin = clock();
std::vector< A > as;
for ( int i = 0; i < REPETTIMES; ++i ) {
A temp;
as.emplace_back( temp );
}
timeEnd = clock();
std::cout << "use emplace_back&&stack cost=" << ( timeEnd - timeBegin ) / ( CLOCKS_PER_SEC / 1000 ) << std::endl;
}
{
timeBegin = clock();
std::vector< A > as;
for ( int i = 0; i < REPETTIMES; ++i ) {
as.push_back( *new A );
}
timeEnd = clock();
std::cout << "use push_back&&new cost=" << ( timeEnd - timeBegin ) / ( CLOCKS_PER_SEC / 1000 ) << std::endl;
}
{
timeBegin = clock();
std::vector< A > as;
for ( int i = 0; i < REPETTIMES; ++i ) {
A temp;
as.push_back( temp );
}
timeEnd = clock();
std::cout << "use push_back&&stack cost=" << ( timeEnd - timeBegin ) / ( CLOCKS_PER_SEC / 1000 ) << std::endl;
}
{
timeBegin = clock();
for ( int i = 0; i < REPETTIMES; ++i ) {
auto a = new A;
}
timeEnd = clock();
std::cout << "use new cost=" << ( timeEnd - timeBegin ) / ( CLOCKS_PER_SEC / 1000 ) << std::endl;
}
{
timeBegin = clock();
auto mp = new MemoryPool< A, REPETTIMES + 1000 >;
for ( int i = 0; i < REPETTIMES; ++i ) {
auto a = mp->newElement();
}
timeEnd = clock();
std::cout << "use app cacay memory pool cost=" << ( timeEnd - timeBegin ) / ( CLOCKS_PER_SEC / 1000 ) << std::endl;
}
//请添加.cpp作为头文件
// {
// timeBegin = clock();
// auto mp = new AppShift::Memory::MemoryPool( ( REPETTIMES + 100 ) * sizeof( A ) );
// for ( int i = 0; i < REPETTIMES; ++i ) {
// auto a = new ( mp ) A;
// }
// timeEnd = clock();
// std::cout << "use app shift memory pool cost=" << ( timeEnd - timeBegin ) / ( CLOCKS_PER_SEC / 1000 ) << std::endl;
// }
{
timeBegin = clock();
for ( int i = 0; i < REPETTIMES; ++i ) {
A temp;
}
timeEnd = clock();
std::cout << "use stack cost=" << ( timeEnd - timeBegin ) / ( CLOCKS_PER_SEC / 1000 ) << std::endl;
}
return 0;
}