Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Re: Аллокатор с отдельными пулами памяти для разных тредов
Здравствуйте, chukichuki, Вы писали:
C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Здравствуйте, chukichuki, Вы писали:
C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Врядли такой найдется.
Аллокаторы общего назначения, обычно, позволяют передавать владение указателем через границу потока. Это допущение противоречит твоим требованиям , так что увы.
Re[2]: Аллокатор с отдельными пулами памяти для разных тредо
Здравствуйте, remark, Вы писали:
R>Здравствуйте, chukichuki, Вы писали:
C>>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
R>
Спасибо. Но это видимо не совсем то, что я ищу. Как понимаю менеджмент памяти происходит на уровне ядра. Следовательно, работает медленно, да еще и непортабельно (или я ошибаюсь ?) Хочется чего-то работающего в юзерспейсе.
Re[3]: Аллокатор с отдельными пулами памяти для разных тредо
Здравствуйте, chukichuki, Вы писали:
C>Спасибо. Но это видимо не совсем то, что я ищу. Как понимаю менеджмент памяти происходит на уровне ядра. Следовательно, работает медленно, да еще и непортабельно (или я ошибаюсь ?) Хочется чего-то работающего в юзерспейсе.
Непортабельно — это да. Все остальные, кроме MS, почему-то не додумались реализовать аллокатор как отдельную сущность, а потом обернуть malloc/free вокруг него.
А касательно ядра и скорости, то тут всё нормально. Виндовый хип реализован в пространстве пользователя. Ну собственно, а что по-твоему malloc/free/new/delete в Windows вызывают? Они и вызывают HeapAlloc/HeapFree, передавая в него глобальный на процесс хендл хипа. Только плюс там идут дополнительные издержки на захват/освобождение мьютекса.
Здравствуйте, chukichuki, Вы писали:
C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Вот попробуй мой велосипед :
Полностью потокобезопасен, имеет всего три метода, при желании можно добавить нужные.
h.File:
////////////////////////////////////////////////////////////////////////
// class MemoryAllocator
////////////////////////////////////////////////////////////////////////class MemoryAllocator : public Mt::IAllocator
{
public:
////////////////////////////////////////////////////////////////////
// Construction/Destruction
MemoryAllocator(size_t nMaxAllocationBlockSize);
virtual ~MemoryAllocator();
////////////////////////////////////////////////////////////////////
// Implementation of IAllocator interfacevirtual void* Allocate(size_t nSize);
virtual void Free(void* pPointer);
virtual void Erase();
////////////////////////////////////////////////////////////////////
// Public methodsprivate:
/////////////////////////////////////////////////////////////////////
// Internal constants and type definitionsstruct 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 sizelong 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);
/////////////////////////////////////////////////////////////////////
// Implementationstatic 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;
/////////////////////////////////////////////////////////////////////
// Attributesconst size_t m_nMemoryPageSize; // Memory page sizeconst size_t m_nMinBlockSize; // Allocation block minimum size const size_t m_nMaxBlockSize; // Allocation block maximum size
FreePools m_pools; // Memory block poolsmutable 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 interfacevoid* MemoryAllocator::Allocate(size_t nSize)
{
MemoryPool* pMemoryPool = GetPool(nSize);
// Check does pool for specified block size existsif (pMemoryPool == NULL)
return NULL; // Block size is too large
// Statistic
LockFreeINC(pMemoryPool->nOccupied);
// Return newly allocated blockwhile (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 blockreturn 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 poolwhile (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 zerowhile (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 numberwhile (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 arrayreturn 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 foundreturn 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 memoryreturn MemoryBlockToPointer(pMemoryBlock);
}
Re: Аллокатор с отдельными пулами памяти для разных тредов
Здравствуйте, chukichuki, Вы писали:
C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Посмотри apache portable runtime. Пулы памяти есть, потоки есть, пулы потоков есть. Много еще чего есть. Это целый фреймворк, все кроссплатформенно
Re: Аллокатор с отдельными пулами памяти для разных тредов
Здравствуйте, chukichuki, Вы писали:
C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Делал что-то подобное для переопределённых new/delete. Хотелось снизить нагрузку по синхронизации для часто выделяемых в пуле потоков структур. Иногда эти структуры в одном потоке и жили, но даже если удаление происходило в другом потоке, всё равно пул мьютексов лучше одного. Сделал через параметризованный new, параметром передавал номер потока в пуле, а дальше уже выбирался нужный аллокатор из массива. Для удаления памяти резервировал чуть больше и рядом с выделенным объектом прописывал номер использованного аллокатора.