きままにブログ

プログラミングを主とした私のメモ帳です。寂しいのでコメントください笑

簡易メモリアロケータのunique_ptrを返す実装

#pragma once

#include <vector>
#include <memory>

namespace mytools {
template <typename T>
class FixedVector {
	template <typename T>
	struct Storage {
		Storage(size_t size) : buffer(new T[size]), next(nullptr) {}
		~Storage() {
			delete[] buffer; delete next;
		}
		T* buffer;
		Storage<T>* next;
	};

public:
	template <typename T>
	class Deleter {
	public:
		FixedVector<T>* self;
		using pointer = T*;
		Deleter(FixedVector<T>* self) : self(self) {}

		void operator()(pointer p) {
			self->free(p);
		}
	};

	using unique = std::unique_ptr<T, Deleter<T>>;

	FixedVector(size_t size = 10) : size(size), free_list_size(size), position(0), free_top(0) {
		storage = new Storage<T>(size);
		last_storage = storage;
		free_list.resize(free_list_size);
		popAndPush(&storage->buffer[0]);
	}

	FixedVector(const FixedVector<T>& it) {}

	FixedVector(FixedVector<T>&& it) {}

	template <typename ...Args>
	T* make(Args ...args) {
		T* ret = getTop();

		new(ret)T(args...);

		if (free_top > 0) {
			pop();
		}
		else {
			++position;

			if (position >= size) {
				makeStorage();
			}

			popAndPush(&last_storage->buffer[position]);
		}

		return ret;
	}

	template <typename ...Args>
	unique make_unique(Args ...args) {
		return unique(make(args...), Deleter<T>(this));
	}

	void free(T* it) {
		push(it);
	}

	void sfree(T*& it) {
		if (it == nullptr) {
			throw std::exception();
		}
		push(it);
		it = nullptr;
	}

	~FixedVector() {
		delete storage;
	}

private:
	T* getTop() {
		return free_list[free_top];
	}

	void pop() {
		--free_top;
	}

	void popAndPush(T* it) {
		free_list[free_top] = it;
	}

	void push(T* it) {
		++free_top;
		if (free_top >= free_list_size) {
			free_list_size += size;
			free_list.resize(free_list_size);
		}
		free_list[free_top] = it;
	}

	void makeStorage() {
		last_storage = new Storage<T>(size);
		storage->next = last_storage;
		position = 0;
	}

	Storage<T>* storage;
	Storage<T>* last_storage;
	std::vector<T*> free_list;
	size_t size;
	size_t free_top;
	size_t position;
	size_t free_list_size;
};
}

について、ベンチマークを取ったところ:

mytools::FixedVector<S> fv;
	{
		WindowsTimer wt;
		wt.start();
		for (int i = 0; i < 10000000; ++i) {
			auto p = fv.make();
			fv.free(p);
		}
		wt.stop();
		printf("MAKE %lf\n", static_cast<double>(wt));

		wt.start();
		for (int i = 0; i < 10000000; ++i) {
			decltype(fv)::unique p = fv.make_unique();
		}
		wt.stop();
		printf("MAKE_UNIQUE %lf\n", static_cast<double>(wt));

		wt.start();
		for (int i = 0; i < 10000000; ++i) {
			auto p = new S();
			delete p;
		}
		wt.stop();
		printf("delete %lf\n", static_cast<double>(wt));

		wt.start();
		for (int i = 0; i < 10000000; ++i) {
			auto p = std::make_unique<S>();
		}
		wt.stop();
		printf("unique %lf\n", static_cast<double>(wt));
	}
MAKE 0.024968
MAKE_UNIQUE 0.096462
delete 0.597430
unique 0.539340

という結果になりました。やはりカスタムデリータに管理元クラスのポインタを持たせているためにだいぶ遅くなってしまっているみたいです。そもそもカスタムデリータが遅いのか?