第一種 自定義allocator
C++03時代的STL容器使用的是被稱為分配器allocator的內存管理模塊。
allocator是無狀態(stateless)的,定義里沒有成員變量,全是成員函數和一些typedef。
自定義allocator細節很多,尤其是那個rebind。
allocator是個畫蛇添足的設計,提供了不必要的靈活性,增加了復雜度,增加了心智負擔。
stl_mem.h
#pragma once
#include <iostream>
#include <list>
#include <map>
#include <unordered_map>
#include <vector>
#include <set>
#include <algorithm>
#include <mutex>
using namespace std;
namespace MEM_STL {
struct memNode
{
memNode *nextnode;
};
class mempool
{
public:
static void *alloc(int size)
{
printf("alloc:%d\n", size);
lock_guard<mutex> guard(m_mutex);
int index = getindex(size);
int realsize = 1 << (index + 1);
if (m_free_head[index] == NULL)
{
return malloc(realsize);
}
else
{
void *p = m_free_head[index];
m_free_head[index] = m_free_head[index]->nextnode;
return p;
}
return NULL;
}
static void delloc(void *ptr, int size)
{
printf("delloc:%d\n", size);
lock_guard<mutex> guard(m_mutex);
int index = getindex(size);
memNode *pNew = (memNode *)ptr;
pNew->nextnode = m_free_head[index];
m_free_head[index] = pNew;
}
static void report()
{
lock_guard<mutex> guard(m_mutex);
printf("mempool report\n");
for (int i = 0; i < 32; ++i)
{
int n = 0;
auto p = m_free_head[i];
while (p)
{
n++;
p = p->nextnode;
}
printf("index|%02d|len|%d\n", i, n);
}
}
private:
static int getindex(int size)
{
static const unsigned int sizetable[32] =
{
1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7, 1 << 8, 1 << 9, 1 << 10,
1 << 11, 1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, 1 << 17, 1 << 18, 1 << 19, 1 << 20,
1 << 21, 1 << 22, 1 << 23, 1 << 24, 1 << 25, 1 << 26, 1 << 27, 1 << 28, 1 << 29, 1 << 30,
(unsigned int)1 << 31, 0xFFFFFFFF
};
auto p = lower_bound(sizetable, sizetable + 32, (unsigned int)size);
return distance(sizetable, p);
}
protected:
static memNode *m_free_head[32]; //32個指針
static mutex m_mutex;
};
template<class T>
class my_allocator : public std::allocator<T>
{
public:
typedef std::allocator<T> base_type;
typedef size_t size_type;
typedef T *pointer;
// 必須要重新定義
template<class Other>
struct rebind
{
typedef my_allocator<Other> other;
};
// 構造函數必須實現
my_allocator()
{
}
my_allocator(my_allocator<T> const &)
{
}
my_allocator<T> &operator=(my_allocator<T> const &)
{
return (*this);
}
// 模板
template<class Other>
my_allocator(my_allocator<Other> const &)
{
}
// 模板
template<class Other>
my_allocator<T> &operator=(my_allocator<Other> const &)
{
return (*this);
}
// 內存的分配與釋放可以實現為自定義的算法,替換函數體即可
pointer allocate(size_type count)
{
return (pointer)mempool::alloc(count * sizeof(T));
}
void deallocate(pointer ptr, size_type count)
{
mempool::delloc(ptr, count * sizeof(T));
}
};
template<class _Ty, class _Alloc = my_allocator<_Ty> >
class m_list : public list<_Ty, _Alloc>
{
};
template<class _Kty, class _Ty, class _Pr = less<_Kty>, class _Alloc = my_allocator<pair<const _Kty, _Ty> > >
class m_map : public map<_Kty, _Ty, _Pr, _Alloc>
{
};
template<class _Ty, class _Alloc = my_allocator<_Ty>>
class m_vector : public vector<_Ty, _Alloc>
{
};
template<class _Kty, class _Pr = less<_Kty>, class _Alloc = my_allocator<_Kty> >
class m_set : public set<_Kty, _Pr, _Alloc>
{
};
template<class _Kty, class _Ty, class _HASH = hash<_Kty>, class _KeyEqual = equal_to<_Kty>, class _Alloc = my_allocator<_Kty>>
class m_unordered_map : public unordered_map<_Kty, _Ty, _HASH, _KeyEqual, _Alloc>
{
};
};
stl_mem.cpp
#include "stl_mem.h"
using namespace MEM_STL;
memNode *mempool::m_free_head[32] = { 0 };
mutex mempool::m_mutex = { };
於是C++17引入了pmr(polymorphic memory resource 多態的內存資源)。
第二種 使用C++17提供的pmr
先看一種非常簡單的內存分配實現:
char *buf = 0;
const int max_size = 1024000;
int len = 0;
void *new_(size_t t){
void *p = 0;
if (t + len < max_size){
p = &buf[len];
len += (t + 4);
}
return p;
}
void free_(void *p){}
class buf_helper{
public:
buf_helper(){
buf = new char[max_size];
}
~buf_helper(){
delete buf;
}
};
buf_helper g_buf_helper;
new一塊內存,中途只使用不回收,最后一次性delete掉。
pmr就是這種思路。C++17 pmr
點擊查看代碼
#include <iostream>
#include <cstdlib>
#include <iostream>
#include <memory_resource>
#include <vector>
char *buf = 0;
const int max_size = 1024000;
int len = 0;
void *new_(size_t t)
{
void *p = 0;
if (t + len < max_size)
{
p = &buf[len];
len += (t + 4);
}
return p;
}
void free_(void *p)
{
}
class buf_helper
{
public:
buf_helper()
{
buf = new char[max_size];
}
~buf_helper()
{
delete buf;
}
};
buf_helper g_buf_helper;
class debug_resource : public std::pmr::memory_resource
{
public:
explicit debug_resource(std::pmr::memory_resource *up = std::pmr::get_default_resource())
: _upstream{ up }
{
}
void *do_allocate(size_t bytes, size_t alignment) override
{
std::cout << " do_allocate(): " << bytes << '\n';
return _upstream->allocate(bytes, alignment);
}
void do_deallocate(void *ptr, size_t bytes, size_t alignment) override
{
std::cout << " do_deallocate(): " << bytes << '\n';
_upstream->deallocate(ptr, bytes, alignment);
}
bool do_is_equal(const std::pmr::memory_resource &other) const noexcept override
{
std::cout << " do_is_equal(): " << '\n';
return this == &other;
}
private:
std::pmr::memory_resource *_upstream;
};
int main()
{
char buffer[32] = {};
std::fill_n(std::begin(buffer), std::size(buffer) - 1, '_');
std::cout << buffer << '\n';
debug_resource dr;
std::pmr::monotonic_buffer_resource pool{ std::data(buffer), std::size(buffer) ,&dr };
std::pmr::vector<char> vec{ &pool };
//vec.reserve(26);
for (char ch = 'a'; ch <= 'z'; ++ch)
{
vec.push_back(ch);
std::cout << buffer << '\n';
}
std::cout << buffer << '\n';
}
核心代碼
char buffer[32] = {};
memory_resource dr;
std::pmr::monotonic_buffer_resource pool{ std::data(buffer), std::size(buffer) ,&dr };
std::pmr::vector<char> vec{ &pool };
pmr::memory_resource 決定內存來源
monotonic buffer resource
unsynchronized_pool_resource
synchronized_pool_resource
pmr::polymorphic_allocator 負責內存分配
名字中的polymorphic是說:這個新的分配器的行為會表現出多態,與以前的分配器不同。
新分配器和stl分配器兼容。保存了一個memory_resource的指針,所以是有狀態的(statefull)。
外界需要保證memory_resource指針的生命周期。
polymorphic_allocator 就是把Memory_resource適配成Alloctor。
pmr是專門用來提高c++內存性能的!一般應用,對pmr的需求沒有那么強烈。
一句話:內存局部性原理
可以細讀這篇文章:游戲引擎開發新感覺!(6) c++17內存管理