PODArray.h 8.7 KB
Newer Older
1 2 3 4 5 6 7 8 9
#pragma once

#include <string.h>
#include <malloc.h>
#include <cstddef>
#include <algorithm>
#include <memory>

#include <boost/noncopyable.hpp>
10
#include <boost/iterator_adaptors.hpp>
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

#include <Yandex/likely.h>
#include <Yandex/strong_typedef.h>

#include <DB/Core/Exception.h>
#include <DB/Core/ErrorCodes.h>


namespace DB
{

/** Динамический массив для POD-типов.
  * Предназначен для небольшого количества больших массивов (а не большого количества маленьких).
  * А точнее - для использования в ColumnVector.
  * Отличается от std::vector тем, что не инициализирует элементы.
26 27 28
  *
  * Сделан некопируемым, чтобы не было случайных копий. Скопировать данные можно с помощью метода assign.
  *
29 30
  * Поддерживается только часть интерфейса std::vector.
  *
31 32 33 34 35 36 37 38 39
  * Конструктор по-умолчанию создаёт пустой объект, который не выделяет память.
  * Затем выделяется память минимум под initial_size элементов.
  *
  * При первом выделении памяти использует std::allocator.
  *  В реализации из libstdc++ он кэширует куски памяти несколько больше, чем обычный malloc.
  *
  * При изменении размера, использует realloc, который может (но не обязан) использовать mremap для больших кусков памяти.
  * По факту, mremap используется при использовании аллокатора из glibc, но не используется, например, в tcmalloc.
  *
40 41 42 43 44 45 46
  * Если вставлять элементы push_back-ом, не делая reserve, то PODArray примерно в 2.5 раза быстрее std::vector.
  */
template <typename T>
class PODArray : private boost::noncopyable, private std::allocator<char>	/// empty base optimization
{
private:
	typedef std::allocator<char> Allocator;
P
Merge  
Pavel Kartavyy 已提交
47
	static const size_t initial_size;
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

	char * c_start;
	char * c_end;
	char * c_end_of_storage;

	bool use_libc_realloc;

	T * t_start() 						{ return reinterpret_cast<T *>(c_start); }
	T * t_end() 						{ return reinterpret_cast<T *>(c_end); }
	T * t_end_of_storage() 				{ return reinterpret_cast<T *>(c_end_of_storage); }
	
	const T * t_start() const 			{ return reinterpret_cast<const T *>(c_start); }
	const T * t_end() const 			{ return reinterpret_cast<const T *>(c_end); }
	const T * t_end_of_storage() const 	{ return reinterpret_cast<const T *>(c_end_of_storage); }

	size_t storage_size() const { return c_end_of_storage - c_start; }
	static size_t byte_size(size_t n) { return n * sizeof(T); }

	static size_t round_up_to_power_of_two(size_t n)
	{
		--n;
		n |= n >> 1;
		n |= n >> 2;
		n |= n >> 4;
		n |= n >> 8;
		n |= n >> 16;
		n |= n >> 32;
		++n;

		return n;
	}
	
	static size_t to_size(size_t n) { return byte_size(std::max(initial_size, round_up_to_power_of_two(n))); }
	
	void alloc(size_t n)
	{
84 85 86 87 88 89
		if (n == 0)
		{
			c_start = c_end = c_end_of_storage = NULL;
			return;
		}
		
90 91 92 93 94 95 96 97 98
		size_t bytes_to_alloc = to_size(n);
		c_start = c_end = Allocator::allocate(bytes_to_alloc);
		c_end_of_storage = c_start + bytes_to_alloc;

		//memset(c_start, 0, bytes_to_alloc);
	}

	void dealloc()
	{
99 100 101
		if (c_start == NULL)
			return;
		
102 103 104 105 106 107 108 109 110
		if (use_libc_realloc)
			::free(c_start);
		else
			Allocator::deallocate(c_start, storage_size());
	}

	void realloc(size_t n)
	{
//		std::cerr << "realloc" << std::endl;
111 112 113 114 115 116

		if (c_start == NULL)
		{
			alloc(n);
			return;
		}
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
		
		ptrdiff_t end_diff = c_end - c_start;
		size_t bytes_to_alloc = to_size(n);

		char * old_c_start = c_start;
		char * old_c_end_of_storage = c_end_of_storage;

		if (use_libc_realloc)
		{
			c_start = reinterpret_cast<char *>(::realloc(c_start, bytes_to_alloc));

			if (NULL == c_start)
				throwFromErrno("PODArray: cannot realloc", ErrorCodes::CANNOT_ALLOCATE_MEMORY);
		}
		else
		{
			c_start = reinterpret_cast<char *>(malloc(bytes_to_alloc));

			if (NULL == c_start)
				throwFromErrno("PODArray: cannot realloc", ErrorCodes::CANNOT_ALLOCATE_MEMORY);

			memcpy(c_start, old_c_start, old_c_end_of_storage - old_c_start);
			Allocator::deallocate(old_c_start, old_c_end_of_storage - old_c_start);
		}
		
		//memset(c_start + (old_c_end_of_storage - old_c_start), 0, bytes_to_alloc - (old_c_end_of_storage - old_c_start));
				
		c_end = c_start + end_diff;
		c_end_of_storage = c_start + bytes_to_alloc;

		use_libc_realloc = true;
	}

public:
	typedef T value_type;


	/// Просто typedef нельзя, так как возникает неоднозначность для конструкторов и функций assign.
155
	struct iterator : public boost::iterator_adaptor<iterator, T*>
156 157
	{
		iterator() {}
158
        iterator(T * ptr_) : iterator::iterator_adaptor_(ptr_) {}
159 160
	};

161
	struct const_iterator : public boost::iterator_adaptor<const_iterator, const T*>
162 163
	{
		const_iterator() {}
164
        const_iterator(const T * ptr_) : const_iterator::iterator_adaptor_(ptr_) {}
165 166 167
	};


168
	PODArray() : use_libc_realloc(false) { alloc(0); }
169 170 171 172 173
    PODArray(size_t n) : use_libc_realloc(false) { alloc(n); c_end += byte_size(n); }
    PODArray(size_t n, const T & x) : use_libc_realloc(false) { alloc(n); assign(n, x); }
    PODArray(const_iterator from_begin, const_iterator from_end) : use_libc_realloc(false) { alloc(from_end - from_begin); insert(from_begin, from_end); }
    ~PODArray() { dealloc(); }

174

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
    size_t size() const { return t_end() - t_start(); }
    bool empty() const { return t_end() == t_start(); }
    size_t capacity() const { return t_end_of_storage() - t_start(); }

	T & operator[] (size_t n) 				{ return t_start()[n]; }
	const T & operator[] (size_t n) const 	{ return t_start()[n]; }

	T & front() 			{ return t_start()[0]; }
	T & back() 				{ return t_end()[-1]; }
	const T & front() const { return t_start()[0]; }
	const T & back() const  { return t_end()[-1]; }

	iterator begin() 				{ return t_start(); }
	iterator end() 					{ return t_end(); }
	const_iterator begin() const	{ return t_start(); }
	const_iterator end() const		{ return t_end(); }

	void reserve(size_t n)
	{
		if (n > capacity())
			realloc(n);
	}

	void reserve()
	{
200 201 202 203
		if (size() == 0)
			realloc(initial_size);
		else
			realloc(size() * 2);
204 205 206 207 208 209 210 211
	}

	void resize(size_t n)
	{
		reserve(n);
		c_end = c_start + byte_size(n);
	}

212 213 214 215 216 217 218 219 220 221 222 223
	/// Как resize, но обнуляет новые элементы.
	void resize_fill(size_t n)
	{
		size_t old_size = size();
		if (n > old_size)
		{
			reserve(n);
			memset(c_end, 0, n - old_size);
		}
		c_end = c_start + byte_size(n);
	}

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
	void clear()
	{
		c_end = c_start;
	}

	void push_back(const T & x)
	{
		if (unlikely(c_end == c_end_of_storage))
			reserve();

		*t_end() = x;
		c_end += byte_size(1);
	}

	/// Не вставляйте в массив кусок самого себя. Потому что при ресайзе, итераторы на самого себя могут инвалидироваться.
239 240
	template <typename It1, typename It2>
	void insert(It1 from_begin, It2 from_end)
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
	{
		size_t required_capacity = size() + (from_end - from_begin);
		if (required_capacity > capacity())
			reserve(round_up_to_power_of_two(required_capacity));

		size_t bytes_to_copy = byte_size(from_end - from_begin);
		memcpy(c_end, reinterpret_cast<const void *>(&*from_begin), bytes_to_copy);
		c_end += bytes_to_copy;
	}

	void swap(PODArray<T> & rhs)
	{
		std::swap(c_start, rhs.c_start);
		std::swap(c_end, rhs.c_end);
		std::swap(c_end_of_storage, rhs.c_end_of_storage);
	}

	void assign(size_t n, const T & x)
	{
		resize(n);
		std::fill(begin(), end(), x);
	}

264 265
	template <typename It1, typename It2>
	void assign(It1 from_begin, It2 from_end)
266 267 268 269 270 271
	{
		size_t required_capacity = from_end - from_begin;
		if (required_capacity > capacity())
			reserve(round_up_to_power_of_two(required_capacity));

		size_t bytes_to_copy = byte_size(required_capacity);
272
		memcpy(c_start, reinterpret_cast<const void *>(&*from_begin), bytes_to_copy);
273 274 275 276 277 278 279
		c_end = c_start + bytes_to_copy;
	}

	void assign(const PODArray<T> & from)
	{
		assign(from.begin(), from.end());
	}
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305


	bool operator== (const PODArray<T> & other) const
	{
		if (size() != other.size())
			return false;

		const_iterator this_it = begin();
		const_iterator that_it = other.begin();

		while (this_it != end())
		{
			if (*this_it != *that_it)
				return false;
			
			++this_it;
			++that_it;
		}
		
		return true;
	}

	bool operator!= (const PODArray<T> & other) const
	{
		return !operator==(other);
	}
306 307
};

P
Merge  
Pavel Kartavyy 已提交
308 309 310 311
template <typename T>
const size_t PODArray<T>::initial_size = 4096;


312 313

}