2012-05-31 04:49:55 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
2013-02-08 19:34:44 +00:00
|
|
|
|
#include <memory>
|
2013-02-09 00:12:04 +00:00
|
|
|
|
#include <vector>
|
2015-12-27 10:58:20 +00:00
|
|
|
|
#include <boost/noncopyable.hpp>
|
2015-09-29 19:19:54 +00:00
|
|
|
|
#include <common/likely.h>
|
2015-10-04 06:51:22 +00:00
|
|
|
|
#include <DB/Core/Defines.h>
|
2014-01-04 04:53:07 +00:00
|
|
|
|
#include <DB/Common/ProfileEvents.h>
|
2015-08-16 13:00:22 +00:00
|
|
|
|
#include <DB/Common/Allocator.h>
|
2012-05-31 04:49:55 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
2013-02-08 19:34:44 +00:00
|
|
|
|
/** Пул, в который можно складывать что-нибудь. Например, короткие строки.
|
2012-05-31 04:49:55 +00:00
|
|
|
|
* Сценарий использования:
|
|
|
|
|
* - складываем много строк и запоминаем их адреса;
|
|
|
|
|
* - адреса остаются валидными в течение жизни пула;
|
|
|
|
|
* - при уничтожении пула, вся память освобождается;
|
|
|
|
|
* - память выделяется и освобождается большими кусками;
|
|
|
|
|
* - удаление части данных не предусмотрено;
|
|
|
|
|
*/
|
2015-12-27 10:58:20 +00:00
|
|
|
|
class Arena : private boost::noncopyable
|
2012-05-31 04:49:55 +00:00
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
/// Непрерывный кусок памяти и указатель на свободное место в нём. Односвязный список.
|
2015-11-15 03:09:40 +00:00
|
|
|
|
struct Chunk : private Allocator<false> /// empty base optimization
|
2012-05-31 04:49:55 +00:00
|
|
|
|
{
|
|
|
|
|
char * begin;
|
|
|
|
|
char * pos;
|
|
|
|
|
char * end;
|
|
|
|
|
|
|
|
|
|
Chunk * prev;
|
|
|
|
|
|
|
|
|
|
Chunk(size_t size_, Chunk * prev_)
|
|
|
|
|
{
|
2014-01-04 04:53:07 +00:00
|
|
|
|
ProfileEvents::increment(ProfileEvents::ArenaAllocChunks);
|
|
|
|
|
ProfileEvents::increment(ProfileEvents::ArenaAllocBytes, size_);
|
|
|
|
|
|
2015-08-16 13:00:22 +00:00
|
|
|
|
begin = reinterpret_cast<char *>(Allocator::alloc(size_));
|
2012-05-31 04:49:55 +00:00
|
|
|
|
pos = begin;
|
|
|
|
|
end = begin + size_;
|
|
|
|
|
prev = prev_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~Chunk()
|
|
|
|
|
{
|
2015-08-16 13:00:22 +00:00
|
|
|
|
Allocator::free(begin, size());
|
2014-05-03 22:57:43 +00:00
|
|
|
|
|
2012-05-31 04:49:55 +00:00
|
|
|
|
if (prev)
|
|
|
|
|
delete prev;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t size() { return end - begin; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
size_t growth_factor;
|
2013-09-14 08:11:56 +00:00
|
|
|
|
size_t linear_growth_threshold;
|
2015-02-22 16:09:16 +00:00
|
|
|
|
|
2012-05-31 04:49:55 +00:00
|
|
|
|
/// Последний непрерывный кусок памяти.
|
|
|
|
|
Chunk * head;
|
2013-06-20 12:12:27 +00:00
|
|
|
|
size_t size_in_bytes;
|
2012-05-31 04:49:55 +00:00
|
|
|
|
|
2013-09-14 08:11:56 +00:00
|
|
|
|
static size_t roundUpToPageSize(size_t s)
|
2012-05-31 04:49:55 +00:00
|
|
|
|
{
|
2013-09-14 21:59:52 +00:00
|
|
|
|
return (s + 4096 - 1) / 4096 * 4096;
|
2013-09-14 08:11:56 +00:00
|
|
|
|
}
|
2012-05-31 04:49:55 +00:00
|
|
|
|
|
2013-09-14 08:11:56 +00:00
|
|
|
|
/// Если размер чанка меньше linear_growth_threshold, то рост экспоненциальный, иначе - линейный, для уменьшения потребления памяти.
|
|
|
|
|
size_t nextSize(size_t min_next_size) const
|
|
|
|
|
{
|
|
|
|
|
size_t size_after_grow = 0;
|
2015-02-22 16:09:16 +00:00
|
|
|
|
|
2013-09-14 08:11:56 +00:00
|
|
|
|
if (head->size() < linear_growth_threshold)
|
|
|
|
|
size_after_grow = head->size() * growth_factor;
|
|
|
|
|
else
|
2014-07-04 19:45:06 +00:00
|
|
|
|
size_after_grow = linear_growth_threshold;
|
2013-09-14 08:11:56 +00:00
|
|
|
|
|
|
|
|
|
if (size_after_grow < min_next_size)
|
|
|
|
|
size_after_grow = min_next_size;
|
|
|
|
|
|
|
|
|
|
return roundUpToPageSize(size_after_grow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Добавить следующий непрерывный кусок памяти размера не меньше заданного.
|
2015-10-04 06:51:22 +00:00
|
|
|
|
void NO_INLINE addChunk(size_t min_size)
|
2013-09-14 08:11:56 +00:00
|
|
|
|
{
|
|
|
|
|
head = new Chunk(nextSize(min_size), head);
|
2013-06-20 12:12:27 +00:00
|
|
|
|
size_in_bytes += head->size();
|
2012-05-31 04:49:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
2013-09-14 08:11:56 +00:00
|
|
|
|
Arena(size_t initial_size_ = 4096, size_t growth_factor_ = 2, size_t linear_growth_threshold_ = 128 * 1024 * 1024)
|
|
|
|
|
: growth_factor(growth_factor_), linear_growth_threshold(linear_growth_threshold_),
|
2014-04-08 07:58:53 +00:00
|
|
|
|
head(new Chunk(initial_size_, nullptr)), size_in_bytes(head->size())
|
2012-05-31 04:49:55 +00:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-08 19:34:44 +00:00
|
|
|
|
~Arena()
|
2012-05-31 04:49:55 +00:00
|
|
|
|
{
|
|
|
|
|
delete head;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-08 19:34:44 +00:00
|
|
|
|
/// Получить кусок памяти, без выравнивания.
|
2012-05-31 04:49:55 +00:00
|
|
|
|
char * alloc(size_t size)
|
|
|
|
|
{
|
|
|
|
|
if (unlikely(head->pos + size > head->end))
|
|
|
|
|
addChunk(size);
|
|
|
|
|
|
|
|
|
|
char * res = head->pos;
|
|
|
|
|
head->pos += size;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-22 16:09:16 +00:00
|
|
|
|
/** Отменить только что сделанное выделение памяти.
|
2016-03-12 05:59:31 +00:00
|
|
|
|
* Нужно передать размер не больше того, который был только что выделен.
|
2015-02-22 16:09:16 +00:00
|
|
|
|
*/
|
|
|
|
|
void rollback(size_t size)
|
|
|
|
|
{
|
|
|
|
|
head->pos -= size;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-23 17:27:13 +00:00
|
|
|
|
/** Начать или расширить непрерывный кусок памяти.
|
|
|
|
|
* begin - текущее начало куска памяти, если его надо расширить, или nullptr, если его надо начать.
|
|
|
|
|
* Если в чанке не хватило места - скопировать существующие данные в новый кусок памяти и изменить значение begin.
|
|
|
|
|
*/
|
|
|
|
|
char * allocContinue(size_t size, char const *& begin)
|
|
|
|
|
{
|
2015-10-04 06:10:48 +00:00
|
|
|
|
while (unlikely(head->pos + size > head->end))
|
2015-02-23 17:27:13 +00:00
|
|
|
|
{
|
|
|
|
|
char * prev_end = head->pos;
|
|
|
|
|
addChunk(size);
|
|
|
|
|
|
|
|
|
|
if (begin)
|
|
|
|
|
begin = insert(begin, prev_end - begin);
|
2015-10-04 06:10:48 +00:00
|
|
|
|
else
|
|
|
|
|
break;
|
2015-02-23 17:27:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char * res = head->pos;
|
|
|
|
|
head->pos += size;
|
|
|
|
|
|
|
|
|
|
if (!begin)
|
|
|
|
|
begin = res;
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-08 19:34:44 +00:00
|
|
|
|
/// Вставить строку без выравнивания.
|
2012-05-31 04:49:55 +00:00
|
|
|
|
const char * insert(const char * data, size_t size)
|
|
|
|
|
{
|
|
|
|
|
char * res = alloc(size);
|
|
|
|
|
memcpy(res, data, size);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2015-02-22 16:09:16 +00:00
|
|
|
|
|
2013-06-20 12:12:27 +00:00
|
|
|
|
/// Размер выделенного пула в байтах
|
|
|
|
|
size_t size() const
|
|
|
|
|
{
|
|
|
|
|
return size_in_bytes;
|
|
|
|
|
}
|
2012-05-31 04:49:55 +00:00
|
|
|
|
};
|
|
|
|
|
|
2016-05-28 10:15:36 +00:00
|
|
|
|
using ArenaPtr = std::shared_ptr<Arena>;
|
|
|
|
|
using Arenas = std::vector<ArenaPtr>;
|
2013-02-09 00:12:04 +00:00
|
|
|
|
|
2012-05-31 04:49:55 +00:00
|
|
|
|
|
|
|
|
|
}
|