Как использовать библиотеку boost :: pool для создания собственного распределителя памяти

Я новичок в boost и хочу знать, как именно библиотеки boost :: pool могут помочь мне в создании собственного распределителя памяти.
И у меня есть два вектора объектов структуры.
Первый вектор имеет структурный тип A, а второй вектор имеет структурный тип B.
Как можно повторно использовать память, выделенную первому вектору, второму вектору.

6

Решение

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:

Все пробеги без утечек и убсан / асан чист.

Обратите внимание, как мы повторно используем один и тот же пул с разными контейнерами разного размера структуры и как мы можем даже использовать его с несколькими действующими контейнерами одновременно, при условии, что типы элементов соответствуют размеру запроса (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

7

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]