Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 116 additions & 22 deletions src/jrd/TempSpace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,69 @@ FB_SIZE_T TempSpace::FileBlock::write(offset_t offset, const void* buffer, FB_SI
return file->write(offset, buffer, length);
}

//
// FreeSegmentBySize class
//

void TempSpace::FreeSegmentBySize::addSegment(Segment* const segment)
{
if (m_items.locate(segment->size))
{
SegmentsStack* const cur = &m_items.current();
segment->next = nullptr;
segment->prev = cur->tail;
cur->tail->next = segment;
cur->tail = segment;
}
else
{
segment->prev = nullptr;
segment->next = nullptr;
m_items.add(SegmentsStack(segment->size, segment));
}
}

void TempSpace::FreeSegmentBySize::removeSegment(Segment* const segment)
{
if (segment->next == nullptr)
{
if (!m_items.locate(segment->size))
fb_assert(false);

SegmentsStack* cur = &m_items.current();
if (segment->prev)
{
segment->prev->next = nullptr;
cur->tail = segment->prev;
segment->prev = nullptr;
}
else
m_items.fastRemove();
}
else
{
if (segment->prev)
segment->prev->next = segment->next;

segment->next->prev = segment->prev;

segment->prev = nullptr;
segment->next = nullptr;
}
}

TempSpace::Segment* TempSpace::FreeSegmentBySize::getSegment(FB_SIZE_T size)
{
// Search through the available space in the not used segments list
if (m_items.locate(locGreatEqual, size))
{
SegmentsStack* const cur = &m_items.current();
fb_assert(cur->tail);
return cur->tail;
}
return nullptr;
}

//
// TempSpace::TempSpace
//
Expand All @@ -140,7 +203,7 @@ TempSpace::TempSpace(MemoryPool& p, const PathName& prefix, bool dynamic)
logicalSize(0), physicalSize(0), localCacheUsage(0),
head(NULL), tail(NULL), tempFiles(p),
initialBuffer(p), initiallyDynamic(dynamic),
freeSegments(p)
freeSegments(p), freeSegmentsBySize(p)
{
if (!tempDirs)
{
Expand Down Expand Up @@ -180,6 +243,9 @@ TempSpace::~TempSpace()
dbb->decTempCacheUsage(localCacheUsage);
}

for (bool found = freeSegments.getFirst(); found; found = freeSegments.getNext())
delete freeSegments.current();

while (tempFiles.getCount())
delete tempFiles.pop();
}
Expand Down Expand Up @@ -473,24 +539,15 @@ offset_t TempSpace::allocateSpace(FB_SIZE_T size)
{
// Find the best available space. This is defined as the smallest free space
// that is big enough. This preserves large blocks.
Segment* best = NULL;

// Search through the available space in the not used segments list
for (bool found = freeSegments.getFirst(); found; found = freeSegments.getNext())
{
Segment* const space = &freeSegments.current();
// If this is smaller than our previous best, use it
if (space->size >= size && (!best || (space->size < best->size))) {
best = space;
}
}
Segment* best = freeSegmentsBySize.getSegment(size);

// If we didn't find any space, allocate it at the end of the file
if (!best)
{
extend(size);
return getSize() - size;
}
freeSegmentsBySize.removeSegment(best);

// Set up the return parameters
const offset_t position = best->position;
Expand All @@ -503,8 +560,11 @@ offset_t TempSpace::allocateSpace(FB_SIZE_T size)
if (!freeSegments.locate(best->position))
fb_assert(false);

delete freeSegments.current();
freeSegments.fastRemove();
}
else
freeSegmentsBySize.addSegment(best);

return position;
}
Expand All @@ -526,37 +586,48 @@ void TempSpace::releaseSpace(offset_t position, FB_SIZE_T size)
if (freeSegments.locate(locEqual, end))
{
// The next segment is found to be adjacent
Segment* const next_seg = &freeSegments.current();
Segment* const next_seg = freeSegments.current();
freeSegmentsBySize.removeSegment(next_seg);

next_seg->position -= size;
next_seg->size += size;

if (freeSegments.getPrev())
{
// Check the prior segment for being adjacent
Segment* const prior_seg = &freeSegments.current();
Segment* const prior_seg = freeSegments.current();
if (position == prior_seg->position + prior_seg->size)
{
freeSegmentsBySize.removeSegment(prior_seg);

next_seg->position -= prior_seg->size;
next_seg->size += prior_seg->size;

delete prior_seg;
freeSegments.fastRemove();
}
}

freeSegmentsBySize.addSegment(next_seg);
return;
}

if (freeSegments.locate(locLess, position))
{
// Check the prior segment for being adjacent
Segment* const prior_seg = &freeSegments.current();
Segment* const prior_seg = freeSegments.current();
if (position == prior_seg->position + prior_seg->size)
{
freeSegmentsBySize.removeSegment(prior_seg);
prior_seg->size += size;
freeSegmentsBySize.addSegment(prior_seg);
return;
}
}

freeSegments.add(Segment(position, size));
Segment* new_seg = FB_NEW_POOL(pool) Segment(position, size);
if (freeSegments.add(new_seg))
freeSegmentsBySize.addSegment(new_seg);
}

//
Expand Down Expand Up @@ -610,14 +681,31 @@ UCHAR* TempSpace::findMemory(offset_t& begin, offset_t end, size_t size) const

bool TempSpace::validate(offset_t& free) const
{
FB_SIZE_T cnt = 0;
free = 0;
FreeSegmentTree::ConstAccessor accessor(&freeSegments);
for (bool found = accessor.getFirst(); found; found = accessor.getNext())
{
const offset_t size = accessor.current().size;
const offset_t size = accessor.current()->size;
fb_assert(size != 0);
free += size;
cnt++;
}

FreeSegmentsStackTree::ConstAccessor stackAccessor(&freeSegmentsBySize.m_items);
for (bool found = stackAccessor.getFirst(); found; found = stackAccessor.getNext())
{
const SegmentsStack* const stack = &stackAccessor.current();
const Segment* cur = stack->tail;
fb_assert(cur->next == NULL);
while (cur)
{
cnt--;
fb_assert(cur->size == stack->size);
cur = cur->prev;
}
}
fb_assert(cnt == 0);

offset_t disk = 0;
for (FB_SIZE_T i = 0; i < tempFiles.getCount(); i++)
Expand All @@ -643,7 +731,7 @@ ULONG TempSpace::allocateBatch(ULONG count, FB_SIZE_T minSize, FB_SIZE_T maxSize
offset_t freeMem = 0;

for (bool found = freeSegments.getFirst(); found; found = freeSegments.getNext())
freeMem += freeSegments.current().size;
freeMem += freeSegments.current()->size;

freeMem = MIN(freeMem / count, maxSize);
freeMem = MAX(freeMem, minSize);
Expand All @@ -653,7 +741,7 @@ ULONG TempSpace::allocateBatch(ULONG count, FB_SIZE_T minSize, FB_SIZE_T maxSize
bool is_positioned = freeSegments.getFirst();
while (segments.getCount() < count && is_positioned)
{
Segment* freeSpace = &freeSegments.current();
Segment* freeSpace = freeSegments.current();
offset_t freeSeek = freeSpace->position;
const offset_t freeEnd = freeSpace->position + freeSpace->size;

Expand All @@ -668,22 +756,25 @@ ULONG TempSpace::allocateBatch(ULONG count, FB_SIZE_T minSize, FB_SIZE_T maxSize
fb_assert(p == mem);
fb_assert(seek1 == freeSeek);
#endif
freeSegmentsBySize.removeSegment(freeSpace);

if (freeSeek != freeSpace->position)
{
const offset_t skip_size = freeSeek - freeSpace->position;
const Segment skip_space(freeSpace->position, skip_size);
Segment* const skip_space = FB_NEW_POOL(pool) Segment(freeSpace->position, skip_size);

freeSpace->position += skip_size;
freeSpace->size -= skip_size;
fb_assert(freeSpace->size != 0);

if (!freeSegments.add(skip_space))
fb_assert(false);
freeSegmentsBySize.addSegment(skip_space);

if (!freeSegments.locate(skip_space.position + skip_size))
if (!freeSegments.locate(freeSpace->position))
fb_assert(false);

freeSpace = &freeSegments.current();
freeSpace = freeSegments.current();
}

SegmentInMemory seg;
Expand All @@ -697,8 +788,11 @@ ULONG TempSpace::allocateBatch(ULONG count, FB_SIZE_T minSize, FB_SIZE_T maxSize

if (!freeSpace->size)
{
delete freeSegments.current();
is_positioned = freeSegments.fastRemove();
}
else
freeSegmentsBySize.addSegment(freeSpace);
}
else
{
Expand Down
52 changes: 45 additions & 7 deletions src/jrd/TempSpace.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,37 @@ class TempSpace : public Firebird::File
class Segment
{
public:
Segment() : position(0), size(0)
{}

Segment(offset_t aPosition, offset_t aSize) :
position(aPosition), size(aSize)
position(aPosition), size(aSize), prev(nullptr), next(nullptr)
{}

offset_t position;
offset_t size;
Segment* prev;
Segment* next;

static const offset_t& generate(const void* /*sender*/, const Segment* segment)
{
return segment->position;
}
};

class SegmentsStack
{
public:
SegmentsStack() : size(0), tail(nullptr)
{}

SegmentsStack(offset_t aSize, Segment* aSegment) :
size(aSize), tail(aSegment)
{}

offset_t size;
Segment* tail;

static const offset_t& generate(const void* /*sender*/, const Segment& segment)
static const offset_t& generate(const void* /*sender*/, const SegmentsStack& segment)
{
return segment.position;
return segment.size;
}
};

Expand All @@ -209,8 +227,28 @@ class TempSpace : public Firebird::File
Firebird::Array<UCHAR> initialBuffer;
bool initiallyDynamic;

typedef Firebird::BePlusTree<Segment, offset_t, Segment> FreeSegmentTree;
typedef Firebird::BePlusTree<Segment*, offset_t, Segment> FreeSegmentTree;
typedef Firebird::BePlusTree<SegmentsStack, offset_t, SegmentsStack> FreeSegmentsStackTree;

class FreeSegmentBySize
{
friend bool TempSpace::validate(offset_t& freeSize) const;

public:
FreeSegmentBySize(MemoryPool& pool)
: m_items(pool)
{}

void addSegment(Segment* const segment);
void removeSegment(Segment* const segment);
Segment* getSegment(FB_SIZE_T size);

private:
FreeSegmentsStackTree m_items;
};

FreeSegmentTree freeSegments;
FreeSegmentBySize freeSegmentsBySize;

static Firebird::GlobalPtr<Firebird::Mutex> initMutex;
static Firebird::TempDirectoryList* tempDirs;
Expand Down
Loading