moya-lang / Allocator

Ultra fast C++11 allocator for STL containers.
BSD 2-Clause "Simplified" License
273 stars 25 forks source link

MSVC C++17 implementation #11

Closed DBJDBJ closed 3 years ago

DBJDBJ commented 4 years ago

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
moya-mmoczala commented 3 years ago

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.

DBJDBJ commented 3 years ago

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

moya-mmoczala commented 3 years ago

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.