Как использовать библиотеку boost::pool для создания собственного распределителя памяти
Я новичок в boost и хочу знать, как именно библиотеки boost::pool могут помочь мне в создании собственного распределителя памяти. И у меня есть два вектора объектов структуры. Первый вектор имеет структурный тип A, а второй вектор имеет структурный тип B. Как я могу использовать память, выделенную первому вектору, второму вектору.
1 ответ
Boost Pool - это библиотека, которая определяет несколько типов распределителей.
Очевидно, что целью библиотеки является обеспечение пула распределителей.
Выделители пула светятся, когда вы выделяете объекты одинакового размера.
Примечание Если ваша структура
A
и структураB
не идентичны / очень похожего размера, вам может не понравиться это предположение о дизайне.
Распределители, предоставляемые платформой, работают с одноэлементными пулами, и они различаются по размеру вашего контейнера value_type. Это немного негибко, если вы хотите повторно использовать или даже разделять пул между различными типами значений. Кроме того, одноэлементные пулы могут быть негибкими и подразумевать затраты на безопасность потоков.
Итак, я хотел посмотреть, смогу ли я создать самый простой распределитель, который устраняет некоторые из этих проблем.
Я использовал источник для boost::pool_alloc
и пример cppreference как вдохновение, а затем провел некоторое тестирование и профилирование памяти.
Более гибкий распределитель состояний
Вот простейший распределитель пула, о котором я мог подумать:
using Pool = boost::pool<boost::default_user_allocator_malloc_free>;
template <typename T> struct my_pool_alloc {
using value_type = T;
my_pool_alloc(Pool& pool) : _pool(pool) {
assert(pool_size() >= sizeof(T));
}
template <typename U>
my_pool_alloc(my_pool_alloc<U> const& other) : _pool(other._pool) {
assert(pool_size() >= sizeof(T));
}
T *allocate(const size_t n) {
T* ret = static_cast<T*>(_pool.ordered_malloc(n));
if (!ret && n) throw std::bad_alloc();
return ret;
}
void deallocate(T* ptr, const size_t n) {
if (ptr && n) _pool.ordered_free(ptr, n);
}
// for comparing
size_t pool_size() const { return _pool.get_requested_size(); }
private:
Pool& _pool;
};
template <class T, class U> bool operator==(const my_pool_alloc<T> &a, const my_pool_alloc<U> &b) { return a.pool_size()==b.pool_size(); }
template <class T, class U> bool operator!=(const my_pool_alloc<T> &a, const my_pool_alloc<U> &b) { return a.pool_size()!=b.pool_size(); }
Заметки:
- Этот распределитель является отслеживающим состояние и, следовательно, требует реализации контейнеров, которая позволяет им (например, Boost Container, Boost MultiIndex). Теоретически все C++11-совместимые стандартные библиотеки также должны их поддерживать
- Сравнения должны помочь контейнерам поменять / скопировать распределитель или нет. Хотя это не та область, о которой я много думаю, и выбранный подход для маркировки всех пулов разных запрашиваемых размеров может оказаться неадекватным для некоторых.
Образец, Тесты
На моих компиляторах это работает для обоих std::vector
и Boost's vector
:
- Live On Coliru - с GCC std:: vector
- Live On Coliru - с поддержкой GCC:: контейнер:: вектор
- Live On Coliru - с Clang std:: vector
- Live On Coliru - с усилением Clang:: контейнер:: вектор
Все пробеги без утечек и убсан / асан чист.
Обратите внимание, как мы повторно используем один и тот же пул с разными контейнерами разного размера структуры и как мы можем даже использовать его с несколькими действующими контейнерами одновременно, при условии, что типы элементов соответствуют размеру запроса (32)
struct A { char data[7]; };
struct B { char data[29]; };
int main() {
//using boost::container::vector;
using std::vector;
Pool pool(32); // 32 should fit both sizeof(A) and sizeof(B)
{
vector<A, my_pool_alloc<A> > v(1024, pool);
v.resize(20480);
};
// pool.release_memory();
{
vector<B, my_pool_alloc<B> > v(1024, pool);
v.resize(20480);
}
// pool.release_memory();
// sharing the pool between multiple live containers
{
vector<A, my_pool_alloc<A> > v(512, pool);
vector<B, my_pool_alloc<B> > w(512, pool);
v.resize(10240);
w.resize(10240);
};
}
профилирование
Использование профилировщика памяти Valgrind показывает, с release_memory
строки закомментированы как показано:
Комментируя в release_memory()
звонки:
Я надеюсь, что это похоже на то, что вы хотели.
Другие идеи: простое раздельное хранение
Этот распределитель использует существующие pool
которые делегируют обратно в malloc/free для выделения памяти по требованию. Чтобы использовать его с фиксированным "царством", вы можете использовать simple_segregated_storage
непосредственно. Эта статья выглядит как хороший стартер https://theboostcpplibraries.com/boost.pool