moya-lang/Allocator

MSVC C++17 implementation

DBJDBJ opened this issue · 3 comments

It is very slow compared to std::allocator. In any case, the rules on how to create allocators are now as the standard requires. This means to compile in DEBUG mode on MSVC one needs to make sure MSVC std lib iterators are not built in a debug mode.

#ifdef _ITERATOR_DEBUG_LEVEL
#if _ITERATOR_DEBUG_LEVEL == 2
#error _ITERATOR_DEBUG_LEVEL must be set to 0
#endif
#endif

#include <memory>

namespace Moya {

    namespace detail {
        template <class T, std::size_t growSize = 1024>
        class MemoryPool
        {
            struct Block
            {
                Block* next{};
            };

            class Buffer
            {
                static const std::size_t blockSize = sizeof(T) > sizeof(Block) ? sizeof(T) : sizeof(Block);
                uint8_t data[blockSize * growSize]{};

            public:

                Buffer* const next{};

                Buffer(Buffer* next) :
                    next(next)
                {
                }

                T* getBlock(std::size_t index)
                {
                    return reinterpret_cast<T*>(&data[blockSize * index]);
                }
            };

            Block* firstFreeBlock = nullptr;
            Buffer* firstBuffer = nullptr;
            std::size_t bufferedBlocks = growSize;


        public:

            MemoryPool() = default;
            MemoryPool(MemoryPool&& memoryPool) = delete;
            MemoryPool(const MemoryPool& memoryPool) = delete;
            MemoryPool operator =(MemoryPool&& memoryPool) = delete;
            MemoryPool operator =(const MemoryPool& memoryPool) = delete;

            ~MemoryPool()
            {
                while (firstBuffer) {
                    Buffer* buffer = firstBuffer;
                    firstBuffer = buffer->next;
                    delete buffer;
                }
            }

            T* allocate()
            {
                if (firstFreeBlock) {
                    Block* block = firstFreeBlock;
                    firstFreeBlock = block->next;
                    return reinterpret_cast<T*>(block);
                }

                if (bufferedBlocks >= growSize) {
                    firstBuffer = new Buffer(firstBuffer);
                    bufferedBlocks = 0;
                }

                return firstBuffer->getBlock(bufferedBlocks++);
            }

            void deallocate(T* pointer)
            {
                Block* block = reinterpret_cast<Block*>(pointer);
                block->next = firstFreeBlock;
                firstFreeBlock = block;
            }
        };
    } // detail

    ///--------------------------------------------------------------------------------------------------------------
    template <class T, std::size_t growSize = 1024>
    class Allocator final :
        private detail::MemoryPool<T, growSize>,
        public std::allocator<T>
    {
        using memory_pool_type = detail::MemoryPool<T, growSize>;

    public:

        using parent = std::allocator<T>;
        using parent::parent;

        template <class U>
        struct rebind
        {
            typedef Allocator<U, growSize> other;
        };


        Allocator() {}

        Allocator(Allocator& allocator) :
            copyAllocator(&allocator)
        {
        }

        template <class U>
        Allocator(const Allocator<U, growSize>& other)
        {
        }

        ~Allocator()
        {
        }

        using pointer = typename parent::pointer;
        using size_type = typename parent::size_type;

        pointer allocate(size_type n)
        {
            if (n < 1)
                return nullptr;

            return memory_pool_type::allocate();
        }

        void deallocate(pointer p, size_type n)
        {
            memory_pool_type::deallocate(p);
        }
    };
} // Moya

Hi, thanks for your comment!
Unfortunately I am about to decide to mark it as incompatible with MSVC.
Differences form one version of MSVC to another makes it too hard to maintain.

That is true indeed. Although. Trying to compete with std:: implementations has just an educational value. I assume.

Why do you think so? The allocator indeed offers much better performance than the std:: version. Did you tried release version on MSVC? std:: is much generic while the Moya allocator makes some assumptions that makes it faster.