Здравствуйте, chukichuki, Вы писали:
C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Здравствуйте, chukichuki, Вы писали:
C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Врядли такой найдется.
Аллокаторы общего назначения, обычно, позволяют передавать владение указателем через границу потока. Это допущение противоречит твоим требованиям , так что увы.
Re: Аллокатор с отдельными пулами памяти для разных тредов
Здравствуйте, chukichuki, Вы писали:
C>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Посмотри apache portable runtime. Пулы памяти есть, потоки есть, пулы потоков есть. Много еще чего есть. Это целый фреймворк, все кроссплатформенно
Аллокатор с отдельными пулами памяти для разных тредов
Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
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>Очень нужен. Причем такой, чтобы всю ранее выделенную память можно было бы освободить единоразово. Очень пригодился бы для освобождения ресурсов при экстренной остановке треда. Такие есть в природе ?
Делал что-то подобное для переопределённых new/delete. Хотелось снизить нагрузку по синхронизации для часто выделяемых в пуле потоков структур. Иногда эти структуры в одном потоке и жили, но даже если удаление происходило в другом потоке, всё равно пул мьютексов лучше одного. Сделал через параметризованный new, параметром передавал номер потока в пуле, а дальше уже выбирался нужный аллокатор из массива. Для удаления памяти резервировал чуть больше и рядом с выделенным объектом прописывал номер использованного аллокатора.