2015-11-17 16:08:31 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
2015-11-19 15:06:00 +00:00
|
|
|
|
#include <DB/Common/Arena.h>
|
2016-03-07 06:18:06 +00:00
|
|
|
|
#include <DB/Common/BitHelpers.h>
|
2016-01-14 02:47:18 +00:00
|
|
|
|
|
2015-11-17 16:08:31 +00:00
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
2015-12-27 10:58:20 +00:00
|
|
|
|
/** В отличие от Arena, позволяет освобождать (для последующего повторного использования)
|
|
|
|
|
* выделенные ранее (не обязательно только что) куски памяти.
|
|
|
|
|
* Для этого, запрашиваемый размер округляется вверх до степени двух
|
|
|
|
|
* (или до 8, если меньше; или используется выделение памяти вне Arena, если размер больше 65536).
|
|
|
|
|
* При освобождении памяти, для каждого размера (всего 14 вариантов: 8, 16... 65536),
|
|
|
|
|
* поддерживается односвязный список свободных блоков.
|
|
|
|
|
* При аллокации, мы берём голову списка свободных блоков,
|
|
|
|
|
* либо, если список пуст - выделяем новый блок, используя Arena.
|
|
|
|
|
*/
|
|
|
|
|
class ArenaWithFreeLists : private Allocator<false>, private boost::noncopyable
|
2015-11-17 16:08:31 +00:00
|
|
|
|
{
|
|
|
|
|
private:
|
2015-12-27 10:58:20 +00:00
|
|
|
|
/// Если блок свободен, то в его начале хранится указатель на следующий свободный блок, либо nullptr, если свободных блоков больше нет.
|
|
|
|
|
/// Если блок используется, то в нём хранятся какие-то данные.
|
|
|
|
|
union Block
|
2015-11-17 16:08:31 +00:00
|
|
|
|
{
|
2015-12-27 10:58:20 +00:00
|
|
|
|
Block * next;
|
|
|
|
|
char data[0];
|
|
|
|
|
};
|
2015-11-19 13:41:35 +00:00
|
|
|
|
|
2015-12-27 10:58:20 +00:00
|
|
|
|
/// Максимальный размер куска памяти, который выделяется с помощью Arena. Иначе используем Allocator напрямую.
|
|
|
|
|
static constexpr size_t max_fixed_block_size = 65536;
|
2015-11-23 19:04:15 +00:00
|
|
|
|
|
2015-12-27 10:58:20 +00:00
|
|
|
|
/// Получить индекс в массиве freelist-ов для заданного размера.
|
|
|
|
|
static size_t findFreeListIndex(const size_t size)
|
2015-11-23 19:04:15 +00:00
|
|
|
|
{
|
2016-08-07 06:10:15 +00:00
|
|
|
|
return size <= 8 ? 2 : bitScanReverse(size - 1);
|
2015-11-23 19:04:15 +00:00
|
|
|
|
}
|
2015-11-17 16:08:31 +00:00
|
|
|
|
|
2015-12-27 10:58:20 +00:00
|
|
|
|
/// Для выделения блоков не слишком большого размера используется Arena.
|
2015-11-19 15:06:00 +00:00
|
|
|
|
Arena pool;
|
2015-11-17 16:08:31 +00:00
|
|
|
|
|
2015-12-27 10:58:20 +00:00
|
|
|
|
/// Списки свободных блоков. Каждый элемент указывает на голову соответствующего списка, либо равен nullptr.
|
|
|
|
|
/// Первые два элемента не используются, а предназначены для упрощения арифметики.
|
|
|
|
|
Block * free_lists[16] {};
|
2015-11-17 16:08:31 +00:00
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
ArenaWithFreeLists(
|
2015-12-27 10:58:20 +00:00
|
|
|
|
const size_t initial_size = 4096, const size_t growth_factor = 2,
|
|
|
|
|
const size_t linear_growth_threshold = 128 * 1024 * 1024)
|
2015-11-19 15:06:00 +00:00
|
|
|
|
: pool{initial_size, growth_factor, linear_growth_threshold}
|
2015-11-17 16:08:31 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-27 10:58:20 +00:00
|
|
|
|
char * alloc(const size_t size)
|
2015-11-17 16:08:31 +00:00
|
|
|
|
{
|
2015-12-27 10:58:20 +00:00
|
|
|
|
if (size > max_fixed_block_size)
|
2015-11-19 15:06:00 +00:00
|
|
|
|
return static_cast<char *>(Allocator::alloc(size));
|
2015-11-17 16:08:31 +00:00
|
|
|
|
|
2015-11-19 15:06:00 +00:00
|
|
|
|
/// find list of required size
|
|
|
|
|
const auto list_idx = findFreeListIndex(size);
|
2015-11-17 16:08:31 +00:00
|
|
|
|
|
2015-12-27 10:58:20 +00:00
|
|
|
|
/// Если есть свободный блок.
|
|
|
|
|
if (auto & free_block_ptr = free_lists[list_idx])
|
2015-11-19 15:06:00 +00:00
|
|
|
|
{
|
2015-12-27 10:58:20 +00:00
|
|
|
|
/// Возьмём его. И поменяем голову списка на следующий элемент списка.
|
|
|
|
|
const auto res = free_block_ptr->data;
|
|
|
|
|
free_block_ptr = free_block_ptr->next;
|
2015-11-19 15:06:00 +00:00
|
|
|
|
return res;
|
|
|
|
|
}
|
2015-11-17 16:08:31 +00:00
|
|
|
|
|
2015-11-19 15:06:00 +00:00
|
|
|
|
/// no block of corresponding size, allocate a new one
|
2015-12-27 10:58:20 +00:00
|
|
|
|
return pool.alloc(1 << (list_idx + 1));
|
2015-11-17 16:08:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-12-27 10:58:20 +00:00
|
|
|
|
void free(char * ptr, const size_t size)
|
2015-11-17 16:08:31 +00:00
|
|
|
|
{
|
2015-12-27 10:58:20 +00:00
|
|
|
|
if (size > max_fixed_block_size)
|
|
|
|
|
return Allocator::free(ptr, size);
|
2015-11-19 15:06:00 +00:00
|
|
|
|
|
|
|
|
|
/// find list of required size
|
|
|
|
|
const auto list_idx = findFreeListIndex(size);
|
|
|
|
|
|
2015-12-27 10:58:20 +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;
|
2015-11-17 16:08:31 +00:00
|
|
|
|
}
|
2015-11-18 11:53:01 +00:00
|
|
|
|
|
|
|
|
|
/// Размер выделенного пула в байтах
|
|
|
|
|
size_t size() const
|
|
|
|
|
{
|
2015-11-19 15:06:00 +00:00
|
|
|
|
return pool.size();
|
2015-11-18 11:53:01 +00:00
|
|
|
|
}
|
2015-11-17 16:08:31 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|