2015-11-18 11:53:01 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <DB/Core/ErrorCodes.h>
|
|
|
|
#include <DB/Common/Arena.h>
|
|
|
|
#include <common/likely.h>
|
|
|
|
#include <ext/range.hpp>
|
|
|
|
#include <ext/size.hpp>
|
|
|
|
#include <ext/bit_cast.hpp>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
|
2015-12-07 16:07:02 +00:00
|
|
|
/** Can allocate memory objects of fixed size with deletion support.
|
2015-12-08 09:16:33 +00:00
|
|
|
* For small `object_size`s allocated no less than getMinAllocationSize() bytes. */
|
2015-11-18 11:53:01 +00:00
|
|
|
class SmallObjectPool
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
struct Block { Block * next; };
|
2015-12-08 09:16:33 +00:00
|
|
|
static constexpr auto getMinAllocationSize() { return sizeof(Block); }
|
2015-11-18 11:53:01 +00:00
|
|
|
|
|
|
|
const std::size_t object_size;
|
|
|
|
Arena pool;
|
2015-11-18 13:31:29 +00:00
|
|
|
Block * free_list{};
|
2015-11-18 11:53:01 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
SmallObjectPool(
|
2015-12-07 16:07:02 +00:00
|
|
|
const std::size_t object_size_, const std::size_t initial_size = 4096, const std::size_t growth_factor = 2,
|
2015-11-18 11:53:01 +00:00
|
|
|
const std::size_t linear_growth_threshold = 128 * 1024 * 1024)
|
2015-12-08 09:16:33 +00:00
|
|
|
: object_size{std::max(object_size_, getMinAllocationSize())},
|
2015-12-07 16:07:02 +00:00
|
|
|
pool{initial_size, growth_factor, linear_growth_threshold}
|
2015-11-18 11:53:01 +00:00
|
|
|
{
|
2015-11-18 13:31:29 +00:00
|
|
|
if (pool.size() < object_size)
|
|
|
|
return;
|
|
|
|
|
2015-11-18 11:53:01 +00:00
|
|
|
const auto num_objects = pool.size() / object_size;
|
|
|
|
auto head = free_list = ext::bit_cast<Block *>(pool.alloc(num_objects * object_size));
|
|
|
|
|
|
|
|
for (const auto i : ext::range(0, num_objects - 1))
|
|
|
|
{
|
|
|
|
(void) i;
|
|
|
|
head->next = ext::bit_cast<Block *>(ext::bit_cast<char *>(head) + object_size);
|
|
|
|
head = head->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
head->next = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
char * alloc()
|
|
|
|
{
|
|
|
|
if (free_list)
|
|
|
|
{
|
|
|
|
const auto res = reinterpret_cast<char *>(free_list);
|
|
|
|
free_list = free_list->next;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pool.alloc(object_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void free(const void * ptr)
|
|
|
|
{
|
|
|
|
union {
|
|
|
|
const void * p_v;
|
|
|
|
Block * block;
|
|
|
|
};
|
|
|
|
|
|
|
|
p_v = ptr;
|
|
|
|
block->next = free_list;
|
|
|
|
|
|
|
|
free_list = block;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Размер выделенного пула в байтах
|
|
|
|
size_t size() const
|
|
|
|
{
|
|
|
|
return pool.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
}
|