Аллокатор с отдельными пулами памяти для разных тредов
От: chukichuki  
Дата: 19.10.09 13:00
Оценка:
Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Re: Аллокатор с отдельными пулами памяти для разных тредов
От: remark Россия http://www.1024cores.net/
Дата: 19.10.09 13:12
Оценка: 3 (1) +1 -1
Здравствуйте, chukichuki, Вы писали:

C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?


__declspec(thread) HANDLE thread_heap;

void* thread_alloc(size_t sz)
{
  if (thread_heap == 0)
    thread_heap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
  return HeapAlloc(thread_heap, 0, sz);
}

void thread_free(void* p)
{
  HeapFree(thread_heap, 0, p);
}

void on_thread_end()
{
  if (thread_heap)
    HeapDestroy(thread_heap);
}



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Аллокатор с отдельными пулами памяти для разных тредов
От: Chorkov Россия  
Дата: 19.10.09 13:59
Оценка: -1
Здравствуйте, chukichuki, Вы писали:

C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?


Врядли такой найдется.
Аллокаторы общего назначения, обычно, позволяют передавать владение указателем через границу потока. Это допущение противоречит твоим требованиям , так что увы.
Re[2]: Аллокатор с отдельными пулами памяти для разных тредо
От: chukichuki  
Дата: 20.10.09 05:51
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, chukichuki, Вы писали:


C>>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?


R>
R>__declspec(thread) HANDLE thread_heap;

R>void* thread_alloc(size_t sz)
R>{
R>  if (thread_heap == 0)
R>    thread_heap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);
R>  return HeapAlloc(thread_heap, 0, sz);
R>}

R>void thread_free(void* p)
R>{
R>  HeapFree(thread_heap, 0, p);
R>}

R>void on_thread_end()
R>{
R>  if (thread_heap)
R>    HeapDestroy(thread_heap);
R>}
R>


R>


Спасибо. Но это видимо не совсем то, что я ищу. Как понимаю менеджмент памяти происходит на уровне ядра. Следовательно, работает медленно, да еще и непортабельно (или я ошибаюсь ?) Хочется чего-то работающего в юзерспейсе.
Re[3]: Аллокатор с отдельными пулами памяти для разных тредо
От: remark Россия http://www.1024cores.net/
Дата: 20.10.09 06:05
Оценка:
Здравствуйте, chukichuki, Вы писали:

C>Спасибо. Но это видимо не совсем то, что я ищу. Как понимаю менеджмент памяти происходит на уровне ядра. Следовательно, работает медленно, да еще и непортабельно (или я ошибаюсь ?) Хочется чего-то работающего в юзерспейсе.


Непортабельно — это да. Все остальные, кроме MS, почему-то не додумались реализовать аллокатор как отдельную сущность, а потом обернуть malloc/free вокруг него.
А касательно ядра и скорости, то тут всё нормально. Виндовый хип реализован в пространстве пользователя. Ну собственно, а что по-твоему malloc/free/new/delete в Windows вызывают? Они и вызывают HeapAlloc/HeapFree, передавая в него глобальный на процесс хендл хипа. Только плюс там идут дополнительные издержки на захват/освобождение мьютекса.


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: Аллокатор с отдельными пулами памяти для разных тредов
От: AcidTheProgrammer Россия https://hts.tv/
Дата: 20.10.09 08:06
Оценка:
Здравствуйте, chukichuki, Вы писали:

C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?


Вот попробуй мой велосипед :
Полностью потокобезопасен, имеет всего три метода, при желании можно добавить нужные.

h.File:



          ////////////////////////////////////////////////////////////////////////
        // class MemoryAllocator
        ////////////////////////////////////////////////////////////////////////

        class MemoryAllocator : public Mt::IAllocator
        {
        public:

            ////////////////////////////////////////////////////////////////////
            // Construction/Destruction

            MemoryAllocator(size_t nMaxAllocationBlockSize);
            virtual ~MemoryAllocator();

            ////////////////////////////////////////////////////////////////////
            // Implementation of IAllocator interface

            virtual void*                Allocate(size_t nSize);
            virtual void                Free(void* pPointer);
            virtual void                Erase();

            ////////////////////////////////////////////////////////////////////
            // Public methods
            
        private:

            /////////////////////////////////////////////////////////////////////
            // Internal constants and type definitions

            struct MemoryPool;
            struct MemoryBlock
            {
                MemoryPool*                    pPool;        // Pointer to block owned pool
                MemoryBlock*                pNextBlock;    // Pointer to next free memory block
            };

            struct MemoryPool
            {
                size_t                        nMaxSize;    // Pool block maximum size
                long                        nAllocated;    // Number of allocated blocks 
                long                        nOccupied;    // Number of occupied blocks
                MemoryAllocator*            pAllocator;    // Pointer to owner memory allocator
                MemoryBlock*                pFirstBlock;// Pointer to first memory block
            };

            typedef MemoryPool* FreePools; // Holds pointers to first free blocks of memory pools

            /////////////////////////////////////////////////////////////////////
            // Protect class from coping

            MemoryAllocator&            operator=(const MemoryAllocator& reader);

            /////////////////////////////////////////////////////////////////////
            // Implementation

            static size_t                GetMemoryPageSize();
            static MemoryBlock*            PointerToMemoryBlock(const void* pPointer);
            static void*                MemoryBlockToPointer(const MemoryBlock* pMemoryBlock);
            static bool                    LockFreeCAS(MemoryBlock*& pCheck, MemoryBlock* pCompare, MemoryBlock* pNew);
            static long                    LockFreeINC(long& nValue);
            static long                    LockFreeDEC(long& nValue);

            FreePools                     CreatePools() const;
            MemoryPool*                 GetPool(size_t nSize) const;
            void*                        AllocateMemoryBlock(MemoryPool* pMemoryPool) const;
            
            /////////////////////////////////////////////////////////////////////
            // Attributes
        
            const size_t                m_nMemoryPageSize;    // Memory page size
            const size_t                m_nMinBlockSize;    // Allocation block minimum size 
            const size_t                m_nMaxBlockSize;    // Allocation block maximum size 
            FreePools                     m_pools;            // Memory block pools
            mutable size_t                m_nPools;            // Number of created pools
        };


cpp.File:


                ////////////////////////////////////////////////////////////////////////
        // class MemoryAllocator
        ////////////////////////////////////////////////////////////////////////

        ////////////////////////////////////////////////////////////////////////
        // Construction/Destruction

        MemoryAllocator::MemoryAllocator(size_t nMaxAllocationBlockSize)
        :    m_nMemoryPageSize(GetMemoryPageSize())
        ,    m_nMinBlockSize(m_nMemoryPageSize / 8)
        ,    m_nMaxBlockSize(nMaxAllocationBlockSize)
        ,    m_pools(CreatePools())
        {
        }

        MemoryAllocator::~MemoryAllocator()
        {
            Erase();

            // Release pool array

            ::free(m_pools);
            m_pools = NULL;
        }



        ////////////////////////////////////////////////////////////////////////
        // Implementation of IAllocator interface

        void* MemoryAllocator::Allocate(size_t nSize)
        {
            MemoryPool* pMemoryPool = GetPool(nSize);

            // Check does pool for specified block size exists
            if (pMemoryPool == NULL)
                return NULL; // Block size is too large

            // Statistic

            LockFreeINC(pMemoryPool->nOccupied);

            // Return newly allocated block

            while (true) {

                // Check if pool already has free memory block

                MemoryBlock* pFreeBlock = pMemoryPool->pFirstBlock;
                if (pFreeBlock == NULL) {
                    // Has no free block 
                    // So we necessary to allocate new block
                    return AllocateMemoryBlock(pMemoryPool);
                }

                // Pool already has allocated free block(s)
                // Trying to get first free block from pool
                
                MemoryBlock* pNextBlock = pFreeBlock->pNextBlock;
                if (LockFreeCAS(pMemoryPool->pFirstBlock, pFreeBlock, pNextBlock)) {

                    pFreeBlock->pNextBlock = NULL;
                    return MemoryBlockToPointer(pFreeBlock);
                }

                // Block got by another thread. Trying again
            }
        }



        ////////////////////////////////////////////////////////////////////////
        void MemoryAllocator::Free(void* pPointer)
        {
            if (pPointer == NULL)
                return; // Do nothing on NULL pointer

            // Get memory block pool

            MemoryBlock* pMemoryBlock = PointerToMemoryBlock(pPointer);
            MemoryPool* pMemoryPool = pMemoryBlock->pPool;

            ASSERT(pMemoryPool != NULL);
            ASSERT(pMemoryBlock->pNextBlock == NULL);

            // Trying to return memory block to free lock pool

            while (true) {
            
                MemoryBlock* pNextBlock = pMemoryPool->pFirstBlock;
                pMemoryBlock->pNextBlock = pNextBlock;

                if (LockFreeCAS(pMemoryPool->pFirstBlock, pNextBlock, pMemoryBlock))
                    break; // Memory block successfully returned to memory pool

                // Another thread return it memory block. Trying again
            }

            // Statistic

            LockFreeDEC(pMemoryPool->nOccupied);
        }



        ////////////////////////////////////////////////////////////////////////
        void MemoryAllocator::Erase()
        {
            TRACE(L"\nAllocator Erase statistic:");
            LONGLONG nTotalKBUsed = 0;

            for (size_t n = 0; n < m_nPools; n++) {
                MemoryPool memoryPool;

                //Trying to set pool first block to zero

                while (true) {
                    
                    memoryPool = m_pools[n];
                    MemoryBlock* pFirstBlock = memoryPool.pFirstBlock;

                    if (LockFreeCAS(m_pools[n].pFirstBlock, pFirstBlock, NULL))
                        break; // Pool cleared

                    // Pool changed try again
                }

                // Free memory pool blocks

                size_t nBlockSize = (memoryPool.nMaxSize + sizeof(MemoryBlock)) / 1024;
                nTotalKBUsed+= memoryPool.nAllocated * nBlockSize;
                
                MemoryBlock* pMemoryBlock = memoryPool.pFirstBlock;
                while (pMemoryBlock != NULL) {

                    char* pPointer = reinterpret_cast<char*>(pMemoryBlock);
                    pMemoryBlock = pMemoryBlock->pNextBlock;

                    ::free(pPointer);
                    pPointer = NULL;

                    // Statistic
                    LockFreeDEC(m_pools[n].nAllocated);
                }

                TRACE(L"\n\t(%6d)Kb block was: allocated (%d) times, already in use (%d) blocks", nBlockSize, memoryPool.nAllocated, memoryPool.nOccupied);
            }

            TRACE(L"\nTotal memory was in use (%I64i)Kb", nTotalKBUsed);
        }



        ////////////////////////////////////////////////////////////////////////
        // Public methods

        ////////////////////////////////////////////////////////////////////////
        // Implementation

        size_t MemoryAllocator::GetMemoryPageSize()
        {
            SYSTEM_INFO si;
            ::GetSystemInfo(&si);

            return si.dwPageSize;
        }



        ////////////////////////////////////////////////////////////////////////
        MemoryAllocator::MemoryBlock* MemoryAllocator::PointerToMemoryBlock(const void* pPointer)
        {
            return const_cast<MemoryBlock*>(reinterpret_cast<const MemoryBlock*>(static_cast<const char*>(pPointer) - sizeof(MemoryBlock)));
        }



        ////////////////////////////////////////////////////////////////////////
        void* MemoryAllocator::MemoryBlockToPointer(const MemoryBlock* pMemoryBlock)
        {
            return const_cast<char*>(reinterpret_cast<const char*>(pMemoryBlock) + sizeof(MemoryBlock));
        }



        ////////////////////////////////////////////////////////////////////////
        bool MemoryAllocator::LockFreeCAS(MemoryBlock*& pCheck, MemoryBlock* pCompare, MemoryBlock* pNew)
        {
            MemoryBlock* pOld = static_cast<MemoryBlock*>(::InterlockedCompareExchangePointer(reinterpret_cast<LPVOID*>(&pCheck), pNew, pCompare));
            return pOld == pCompare;
        }



        ////////////////////////////////////////////////////////////////////////
        long MemoryAllocator::LockFreeINC(long& nValue)
        {
            return ::InterlockedIncrement(&nValue);
        }



        ////////////////////////////////////////////////////////////////////////
        long MemoryAllocator::LockFreeDEC(long& nValue)
        {
            return ::InterlockedDecrement(&nValue);
        }



        ////////////////////////////////////////////////////////////////////////
        MemoryAllocator::FreePools MemoryAllocator::CreatePools() const
        {
            ASSERT(m_nMinBlockSize >= sizeof(MemoryBlock));
            ASSERT(m_nMinBlockSize <= m_nMaxBlockSize);

            m_nPools = 0;
            FreePools pools;
            size_t nPoolBlockSize = m_nMinBlockSize;

            // Fill fields equal for all pools

            MemoryPool memoryPool;
            memoryPool.nMaxSize = nPoolBlockSize - sizeof(MemoryBlock);
            memoryPool.nAllocated = 0;
            memoryPool.nOccupied = 0;
            memoryPool.pAllocator = const_cast<MemoryAllocator*>(this);
            memoryPool.pFirstBlock = NULL;

            // Calculate necessary pool number
            
            while (memoryPool.nMaxSize <= m_nMaxBlockSize) {
                memoryPool.nMaxSize = nPoolBlockSize - sizeof(MemoryBlock);

                m_nPools+= 1;
                nPoolBlockSize*= 2;
            }

            if (m_nPools == 0)
                return NULL; // Bas maximum block size

            // Allocate pools array
            pools = reinterpret_cast<FreePools>(malloc(sizeof(MemoryPool) * m_nPools));

            // Fill pool specific fields

            nPoolBlockSize = m_nMinBlockSize;
            memoryPool.nMaxSize = nPoolBlockSize - sizeof(MemoryBlock);

            for (size_t n = 0; n < m_nPools; n++) {
                memoryPool.nMaxSize = nPoolBlockSize - sizeof(MemoryBlock);
                pools[n] = memoryPool;

                nPoolBlockSize*= 2;
            }

            // Return just created pools array
            return pools;
        }



        ////////////////////////////////////////////////////////////////////////
        MemoryAllocator::MemoryPool* MemoryAllocator::GetPool(size_t nSize) const
        {
            // Search pool for specified block size 

            for (size_t n = 0; n < m_nPools; n++) {
                if (nSize <= m_pools[n].nMaxSize)
                    return const_cast<MemoryPool*>(&m_pools[n]);
            }

            // Pool for specified size not found
            return NULL;
        }



        ////////////////////////////////////////////////////////////////////////
        void* MemoryAllocator::AllocateMemoryBlock(MemoryPool* pMemoryPool) const
        {
            ASSERT(pMemoryPool != NULL);

            MemoryBlock* pMemoryBlock = reinterpret_cast<MemoryBlock*>(malloc(pMemoryPool->nMaxSize + sizeof(MemoryBlock)));
            pMemoryBlock->pPool = pMemoryPool;
            pMemoryBlock->pNextBlock = NULL;

            // Statistic
            LockFreeINC(pMemoryPool->nAllocated);

            // Return pointer to user memory
            return MemoryBlockToPointer(pMemoryBlock);
        }
Re: Аллокатор с отдельными пулами памяти для разных тредов
От: flamin  
Дата: 21.10.09 16:16
Оценка: +1
Здравствуйте, chukichuki, Вы писали:

C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?


Посмотри apache portable runtime. Пулы памяти есть, потоки есть, пулы потоков есть. Много еще чего есть. Это целый фреймворк, все кроссплатформенно
Re: Аллокатор с отдельными пулами памяти для разных тредов
От: sokel Россия  
Дата: 06.11.09 07:27
Оценка:
Здравствуйте, chukichuki, Вы писали:

C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?


Делал что-то подобное для переопределённых new/delete. Хотелось снизить нагрузку по синхронизации для часто выделяемых в пуле потоков структур. Иногда эти структуры в одном потоке и жили, но даже если удаление происходило в другом потоке, всё равно пул мьютексов лучше одного. Сделал через параметризованный new, параметром передавал номер потока в пуле, а дальше уже выбирался нужный аллокатор из массива. Для удаления памяти резервировал чуть больше и рядом с выделенным объектом прописывал номер использованного аллокатора.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.