簡易メモリアロケータの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
という結果になりました。やはりカスタムデリータに管理元クラスのポインタを持たせているためにだいぶ遅くなってしまっているみたいです。そもそもカスタムデリータが遅いのか?