Files
secondo/Algebras/ParThread/ConcurrentTupleBuffer/ConcurrentTupleBuffer.cpp

272 lines
6.9 KiB
C++
Raw Normal View History

2026-01-23 17:03:45 +08:00
/*
----
This file is part of SECONDO.
Copyright (C) since 2019, University in Hagen, Faculty of Mathematics
and Computer Science, Database Systems for New Applications.
SECONDO is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
SECONDO is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
----
*/
#include "ConcurrentTupleBuffer.h"
#include "ConcurrentTupleBufferReader.h"
#include "ConcurrentTupleBufferWriter.h"
using namespace std;
namespace parthread
{
ConcurrentTupleBuffer::ConcurrentTupleBuffer(
const ConcurrentTupleBufferSettings settings)
: m_settings(settings), m_availableMemory(settings.TotalBufferSizeInBytes),
m_blockPool(m_settings), m_lastNotificationBufferSize(0),
m_tupleBlockMatrix(NULL),
m_numPartitions(settings.DataPartitioner->NumPartitions()),
m_numTupleBlocks(0)
{
assert(settings.TotalBufferSizeInBytes > 0);
//constrain the memory per block to the maximum of an int, because signed
//numeric types are used for the blocks memory counter
m_defaultMemoryPerBlock = std::min((settings.TotalBufferSizeInBytes /
m_settings.MemoryDistributionFactor), (size_t)INT_MAX);
//initialize block matrix
m_tupleBlockMatrix = new TupleBlockQueuePtr[m_numPartitions];
for (size_t i = 0; i < m_numPartitions; i++)
{
m_tupleBlockMatrix[i] = new TupleBlockQueue();
}
};
ConcurrentTupleBuffer::~ConcurrentTupleBuffer()
{
for (size_t i = 0; i < m_numPartitions; i++)
{
delete m_tupleBlockMatrix[i];
}
delete[] m_tupleBlockMatrix;
}
ConcurrentTupleBufferReader *ConcurrentTupleBuffer::GetTupleBufferReader(
int partitionIndex)
{
ConcurrentTupleBufferReader *reader = NULL;
switch (m_settings.DataPartitioner->DistributionType())
{
case DistributionTypes::SharedPartitions:
reader = new ConcurrentTupleBufferSharedReader(this);
break;
case DistributionTypes::DedicatedPartition:
reader = new ConcurrentTupleBufferDedicatedReader(this, partitionIndex);
break;
}
m_access.lock();
m_readerDirectory.insert(reader);
m_access.unlock();
return reader;
}
ConcurrentTupleBufferWriter *ConcurrentTupleBuffer::GetTupleBufferWriter()
{
ConcurrentTupleBufferWriter *writer = new ConcurrentTupleBufferWriter(
this, m_settings.DataPartitioner.get());
m_access.lock();
m_writerDirectory.insert(writer);
m_access.unlock();
return writer;
}
bool ConcurrentTupleBuffer::AllocateTupleBlocks(
TupleBlockVector &tupleBlockVector)
{
m_access.lock();
//an already allocated tuple must be provided to the tuplebuffer
assert(!tupleBlockVector.IsInitialized());
int blockVectorMemory = DistributeMemory(
m_settings.MinMemoryPerTupleVector);
bool hasMemoryAllocated = blockVectorMemory > 0;
//get next free blocks to fill into vector
if (hasMemoryAllocated)
{
for (size_t i = 0; i < m_numPartitions; i++)
{
tupleBlockVector.SetTupleBlockByPartition(
m_blockPool.GetMemoryTupleBlock(), i, blockVectorMemory);
}
}
m_access.unlock();
return hasMemoryAllocated;
}
int ConcurrentTupleBuffer::DistributeMemory(const size_t minMemoryExpected)
{
size_t blockVectorMemory =
std::min(m_availableMemory, m_defaultMemoryPerBlock);
if (blockVectorMemory >= minMemoryExpected)
{
m_availableMemory -= blockVectorMemory;
}
else
{
blockVectorMemory = 0;
}
return blockVectorMemory;
}
void ConcurrentTupleBuffer::ProvideTupleBlocks(
TupleBlockVector &tupleBlockVector)
{
if (!tupleBlockVector.IsInitialized())
{
return;
}
m_access.lock();
int memory = tupleBlockVector.Shrink();
if ((m_availableMemory + memory) > 0)
{
//necessary additional memory can be compensated by buffer memory
m_availableMemory += memory;
memory = 0;
}
//fill blocks into vector
for (size_t i = 0; i < m_numPartitions; i++)
{
TupleBlockPtr tupleBlock = NULL;
tupleBlockVector.PullTupleBlock(tupleBlock, i);
if (memory < 0)
{
m_availableMemory -= m_settings.MinMemoryPerTupleVector;
TupleBlockPtr oldMemoryTupleBlock = tupleBlock;
tupleBlock = m_blockPool.TransformToPersistentTupleBlock(
tupleBlock, m_settings.MinMemoryPerTupleVector);
memory += tupleBlock->UsedMemorySize();
DeallocateTupleBlock(oldMemoryTupleBlock);
}
if (tupleBlock->IsEmpty())
{
m_blockPool.DestroyTupleBlock(tupleBlock);
}
else
{
m_numTupleBlocks++;
m_tupleBlockMatrix[i]->push(tupleBlock);
}
}
m_access.unlock();
}
bool ConcurrentTupleBuffer::ConsumeTupleBlockByPartition(
TupleBlockPtr &tupleBlock, const size_t partitionIdx)
{
m_access.lock();
bool returnValue = false;
if (!m_tupleBlockMatrix[partitionIdx]->empty())
{
tupleBlock = m_tupleBlockMatrix[partitionIdx]->front();
m_tupleBlockMatrix[partitionIdx]->pop();
m_numTupleBlocks--;
returnValue = true;
}
else
{
if (m_writerDirectory.empty())
{
//If no writer is available and the queue is empty,
//then return an unique block that indicates that no
//more blocks are expected
tupleBlock = m_blockPool.GetEndOfDataStreamBlock();
returnValue = true;
}
else
{
tupleBlock = NULL;
}
}
m_access.unlock();
return returnValue;
}
bool ConcurrentTupleBuffer::ConsumeNextTupleBlock(TupleBlockPtr &tupleBlock)
{
m_access.lock();
int partitionIndex = -1;
size_t maxBlockCount = 0;
for (size_t i = 0; i < m_numPartitions; i++)
{
TupleBlockQueuePtr queue = m_tupleBlockMatrix[i];
size_t size = queue->size();
if (maxBlockCount <= size)
{
partitionIndex = i;
maxBlockCount = size;
}
}
bool returnValue = ConsumeTupleBlockByPartition(tupleBlock, partitionIndex);
m_access.unlock();
return returnValue;
}
void ConcurrentTupleBuffer::DeallocateTupleBlock(TupleBlockPtr &tupleBlock)
{
//deallocate memory of block
m_access.lock();
m_availableMemory += tupleBlock->UsedMemorySize();
//recycle tuple block or delete it
m_blockPool.DestroyTupleBlock(tupleBlock);
m_access.unlock();
tupleBlock = NULL;
}
void ConcurrentTupleBuffer::RemoveWriter(ConcurrentTupleBufferWriter *writer)
{
m_access.lock();
assert(m_writerDirectory.erase(writer));
m_access.unlock();
}
void ConcurrentTupleBuffer::RemoveReader(ConcurrentTupleBufferReader *reader)
{
m_access.lock();
assert(m_readerDirectory.erase(reader) > 0);
m_access.unlock();
}
} // namespace parthread