2015-11-17 16:08:31 +00:00
|
|
|
#pragma once
|
|
|
|
|
2017-04-01 09:19:00 +00:00
|
|
|
#include <Common/Arena.h>
|
|
|
|
#include <Common/BitHelpers.h>
|
2016-01-14 02:47:18 +00:00
|
|
|
|
2015-11-17 16:08:31 +00:00
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
|
2017-05-07 20:25:26 +00:00
|
|
|
/** Unlike Arena, allows you to release (for later re-use)
|
|
|
|
* previously allocated (not necessarily just recently) chunks of memory.
|
|
|
|
* For this, the requested size is rounded up to the power of two
|
|
|
|
* (or up to 8, if less, or using memory allocation outside Arena if the size is greater than 65536).
|
|
|
|
* When freeing memory, for each size (14 options in all: 8, 16 ... 65536),
|
2017-05-09 19:07:35 +00:00
|
|
|
* a single-linked list of free blocks is kept track.
|
2017-05-07 20:25:26 +00:00
|
|
|
* When allocating, we take the head of the list of free blocks,
|
|
|
|
* or, if the list is empty - allocate a new block using Arena.
|
2015-12-27 10:58:20 +00:00
|
|
|
*/
|
|
|
|
class ArenaWithFreeLists : private Allocator<false>, private boost::noncopyable
|
2015-11-17 16:08:31 +00:00
|
|
|
{
|
|
|
|
private:
|
2017-05-07 20:25:26 +00:00
|
|
|
/// If the block is free, then the pointer to the next free block is stored at its beginning, or nullptr, if there are no more free blocks.
|
|
|
|
/// If the block is used, then some data is stored in it.
|
2017-04-01 07:20:54 +00:00
|
|
|
union Block
|
|
|
|
{
|
|
|
|
Block * next;
|
|
|
|
char data[0];
|
|
|
|
};
|
|
|
|
|
2017-05-07 20:25:26 +00:00
|
|
|
/// The maximum size of a piece of memory that is allocated with Arena. Otherwise, we use Allocator directly.
|
2017-04-01 07:20:54 +00:00
|
|
|
static constexpr size_t max_fixed_block_size = 65536;
|
|
|
|
|
2017-05-07 20:25:26 +00:00
|
|
|
/// Get the index in the freelist array for the specified size.
|
2017-04-01 07:20:54 +00:00
|
|
|
static size_t findFreeListIndex(const size_t size)
|
|
|
|
{
|
|
|
|
return size <= 8 ? 2 : bitScanReverse(size - 1);
|
|
|
|
}
|
|
|
|
|
2017-05-07 20:25:26 +00:00
|
|
|
/// Arena is used to allocate blocks that are not too large.
|
2017-04-01 07:20:54 +00:00
|
|
|
Arena pool;
|
|
|
|
|
2017-05-07 20:25:26 +00:00
|
|
|
/// Lists of free blocks. Each element points to the head of the corresponding list, or is nullptr.
|
|
|
|
/// The first two elements are not used, but are intended to simplify arithmetic.
|
2017-04-01 07:20:54 +00:00
|
|
|
Block * free_lists[16] {};
|
2015-11-17 16:08:31 +00:00
|
|
|
|
|
|
|
public:
|
2017-04-01 07:20:54 +00:00
|
|
|
ArenaWithFreeLists(
|
|
|
|
const size_t initial_size = 4096, const size_t growth_factor = 2,
|
|
|
|
const size_t linear_growth_threshold = 128 * 1024 * 1024)
|
|
|
|
: pool{initial_size, growth_factor, linear_growth_threshold}
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
char * alloc(const size_t size)
|
|
|
|
{
|
|
|
|
if (size > max_fixed_block_size)
|
|
|
|
return static_cast<char *>(Allocator::alloc(size));
|
|
|
|
|
|
|
|
/// find list of required size
|
|
|
|
const auto list_idx = findFreeListIndex(size);
|
|
|
|
|
2017-05-07 20:25:26 +00:00
|
|
|
/// If there is a free block.
|
2017-04-01 07:20:54 +00:00
|
|
|
if (auto & free_block_ptr = free_lists[list_idx])
|
|
|
|
{
|
2017-05-07 20:25:26 +00:00
|
|
|
/// Let's take it. And change the head of the list to the next item in the list.
|
2017-04-01 07:20:54 +00:00
|
|
|
const auto res = free_block_ptr->data;
|
|
|
|
free_block_ptr = free_block_ptr->next;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// no block of corresponding size, allocate a new one
|
2017-08-10 23:25:51 +00:00
|
|
|
return pool.alloc(1ULL << (list_idx + 1));
|
2017-04-01 07:20:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void free(char * ptr, const size_t size)
|
|
|
|
{
|
|
|
|
if (size > max_fixed_block_size)
|
|
|
|
return Allocator::free(ptr, size);
|
|
|
|
|
|
|
|
/// find list of required size
|
|
|
|
const auto list_idx = findFreeListIndex(size);
|
|
|
|
|
2017-05-07 20:25:26 +00:00
|
|
|
/// Insert the released block into the head of the list.
|
2017-04-01 07:20:54 +00:00
|
|
|
auto & free_block_ptr = free_lists[list_idx];
|
|
|
|
const auto old_head = free_block_ptr;
|
|
|
|
free_block_ptr = reinterpret_cast<Block *>(ptr);
|
|
|
|
free_block_ptr->next = old_head;
|
|
|
|
}
|
|
|
|
|
2017-05-07 20:25:26 +00:00
|
|
|
/// Size of the allocated pool in bytes
|
2017-04-01 07:20:54 +00:00
|
|
|
size_t size() const
|
|
|
|
{
|
|
|
|
return pool.size();
|
|
|
|
}
|
2015-11-17 16:08:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
}
|