Skip to content

Commit 8d6c46e

Browse files
XaBbl4Andrey Kravchenko
andauthored
Add pointers tree to TempSpace class (#8421)
* Add pointers tree to TempSpace class Necessary for faster search of a free segment of the required size. When using temporary blobs, there are situations when a large number of free segments of a small size accumulate during one transaction. * Replace NULL to nullptr * Refactor class and fix server crash * Rename head to tail for better understanding Also add consistency check in validate function --------- Co-authored-by: Andrey Kravchenko <[email protected]>
1 parent be8151e commit 8d6c46e

File tree

2 files changed

+161
-29
lines changed

2 files changed

+161
-29
lines changed

src/jrd/TempSpace.cpp

Lines changed: 116 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,69 @@ FB_SIZE_T TempSpace::FileBlock::write(offset_t offset, const void* buffer, FB_SI
129129
return file->write(offset, buffer, length);
130130
}
131131

132+
//
133+
// FreeSegmentBySize class
134+
//
135+
136+
void TempSpace::FreeSegmentBySize::addSegment(Segment* const segment)
137+
{
138+
if (m_items.locate(segment->size))
139+
{
140+
SegmentsStack* const cur = &m_items.current();
141+
segment->next = nullptr;
142+
segment->prev = cur->tail;
143+
cur->tail->next = segment;
144+
cur->tail = segment;
145+
}
146+
else
147+
{
148+
segment->prev = nullptr;
149+
segment->next = nullptr;
150+
m_items.add(SegmentsStack(segment->size, segment));
151+
}
152+
}
153+
154+
void TempSpace::FreeSegmentBySize::removeSegment(Segment* const segment)
155+
{
156+
if (segment->next == nullptr)
157+
{
158+
if (!m_items.locate(segment->size))
159+
fb_assert(false);
160+
161+
SegmentsStack* cur = &m_items.current();
162+
if (segment->prev)
163+
{
164+
segment->prev->next = nullptr;
165+
cur->tail = segment->prev;
166+
segment->prev = nullptr;
167+
}
168+
else
169+
m_items.fastRemove();
170+
}
171+
else
172+
{
173+
if (segment->prev)
174+
segment->prev->next = segment->next;
175+
176+
segment->next->prev = segment->prev;
177+
178+
segment->prev = nullptr;
179+
segment->next = nullptr;
180+
}
181+
}
182+
183+
TempSpace::Segment* TempSpace::FreeSegmentBySize::getSegment(FB_SIZE_T size)
184+
{
185+
// Search through the available space in the not used segments list
186+
if (m_items.locate(locGreatEqual, size))
187+
{
188+
SegmentsStack* const cur = &m_items.current();
189+
fb_assert(cur->tail);
190+
return cur->tail;
191+
}
192+
return nullptr;
193+
}
194+
132195
//
133196
// TempSpace::TempSpace
134197
//
@@ -140,7 +203,7 @@ TempSpace::TempSpace(MemoryPool& p, const PathName& prefix, bool dynamic)
140203
logicalSize(0), physicalSize(0), localCacheUsage(0),
141204
head(NULL), tail(NULL), tempFiles(p),
142205
initialBuffer(p), initiallyDynamic(dynamic),
143-
freeSegments(p)
206+
freeSegments(p), freeSegmentsBySize(p)
144207
{
145208
if (!tempDirs)
146209
{
@@ -180,6 +243,9 @@ TempSpace::~TempSpace()
180243
dbb->decTempCacheUsage(localCacheUsage);
181244
}
182245

246+
for (bool found = freeSegments.getFirst(); found; found = freeSegments.getNext())
247+
delete freeSegments.current();
248+
183249
while (tempFiles.getCount())
184250
delete tempFiles.pop();
185251
}
@@ -473,24 +539,15 @@ offset_t TempSpace::allocateSpace(FB_SIZE_T size)
473539
{
474540
// Find the best available space. This is defined as the smallest free space
475541
// that is big enough. This preserves large blocks.
476-
Segment* best = NULL;
477-
478-
// Search through the available space in the not used segments list
479-
for (bool found = freeSegments.getFirst(); found; found = freeSegments.getNext())
480-
{
481-
Segment* const space = &freeSegments.current();
482-
// If this is smaller than our previous best, use it
483-
if (space->size >= size && (!best || (space->size < best->size))) {
484-
best = space;
485-
}
486-
}
542+
Segment* best = freeSegmentsBySize.getSegment(size);
487543

488544
// If we didn't find any space, allocate it at the end of the file
489545
if (!best)
490546
{
491547
extend(size);
492548
return getSize() - size;
493549
}
550+
freeSegmentsBySize.removeSegment(best);
494551

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

563+
delete freeSegments.current();
506564
freeSegments.fastRemove();
507565
}
566+
else
567+
freeSegmentsBySize.addSegment(best);
508568

509569
return position;
510570
}
@@ -526,37 +586,48 @@ void TempSpace::releaseSpace(offset_t position, FB_SIZE_T size)
526586
if (freeSegments.locate(locEqual, end))
527587
{
528588
// The next segment is found to be adjacent
529-
Segment* const next_seg = &freeSegments.current();
589+
Segment* const next_seg = freeSegments.current();
590+
freeSegmentsBySize.removeSegment(next_seg);
591+
530592
next_seg->position -= size;
531593
next_seg->size += size;
532594

533595
if (freeSegments.getPrev())
534596
{
535597
// Check the prior segment for being adjacent
536-
Segment* const prior_seg = &freeSegments.current();
598+
Segment* const prior_seg = freeSegments.current();
537599
if (position == prior_seg->position + prior_seg->size)
538600
{
601+
freeSegmentsBySize.removeSegment(prior_seg);
602+
539603
next_seg->position -= prior_seg->size;
540604
next_seg->size += prior_seg->size;
605+
606+
delete prior_seg;
541607
freeSegments.fastRemove();
542608
}
543609
}
544610

611+
freeSegmentsBySize.addSegment(next_seg);
545612
return;
546613
}
547614

548615
if (freeSegments.locate(locLess, position))
549616
{
550617
// Check the prior segment for being adjacent
551-
Segment* const prior_seg = &freeSegments.current();
618+
Segment* const prior_seg = freeSegments.current();
552619
if (position == prior_seg->position + prior_seg->size)
553620
{
621+
freeSegmentsBySize.removeSegment(prior_seg);
554622
prior_seg->size += size;
623+
freeSegmentsBySize.addSegment(prior_seg);
555624
return;
556625
}
557626
}
558627

559-
freeSegments.add(Segment(position, size));
628+
Segment* new_seg = FB_NEW_POOL(pool) Segment(position, size);
629+
if (freeSegments.add(new_seg))
630+
freeSegmentsBySize.addSegment(new_seg);
560631
}
561632

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

611682
bool TempSpace::validate(offset_t& free) const
612683
{
684+
FB_SIZE_T cnt = 0;
613685
free = 0;
614686
FreeSegmentTree::ConstAccessor accessor(&freeSegments);
615687
for (bool found = accessor.getFirst(); found; found = accessor.getNext())
616688
{
617-
const offset_t size = accessor.current().size;
689+
const offset_t size = accessor.current()->size;
618690
fb_assert(size != 0);
619691
free += size;
692+
cnt++;
693+
}
694+
695+
FreeSegmentsStackTree::ConstAccessor stackAccessor(&freeSegmentsBySize.m_items);
696+
for (bool found = stackAccessor.getFirst(); found; found = stackAccessor.getNext())
697+
{
698+
const SegmentsStack* const stack = &stackAccessor.current();
699+
const Segment* cur = stack->tail;
700+
fb_assert(cur->next == NULL);
701+
while (cur)
702+
{
703+
cnt--;
704+
fb_assert(cur->size == stack->size);
705+
cur = cur->prev;
706+
}
620707
}
708+
fb_assert(cnt == 0);
621709

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

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

648736
freeMem = MIN(freeMem / count, maxSize);
649737
freeMem = MAX(freeMem, minSize);
@@ -653,7 +741,7 @@ ULONG TempSpace::allocateBatch(ULONG count, FB_SIZE_T minSize, FB_SIZE_T maxSize
653741
bool is_positioned = freeSegments.getFirst();
654742
while (segments.getCount() < count && is_positioned)
655743
{
656-
Segment* freeSpace = &freeSegments.current();
744+
Segment* freeSpace = freeSegments.current();
657745
offset_t freeSeek = freeSpace->position;
658746
const offset_t freeEnd = freeSpace->position + freeSpace->size;
659747

@@ -668,22 +756,25 @@ ULONG TempSpace::allocateBatch(ULONG count, FB_SIZE_T minSize, FB_SIZE_T maxSize
668756
fb_assert(p == mem);
669757
fb_assert(seek1 == freeSeek);
670758
#endif
759+
freeSegmentsBySize.removeSegment(freeSpace);
760+
671761
if (freeSeek != freeSpace->position)
672762
{
673763
const offset_t skip_size = freeSeek - freeSpace->position;
674-
const Segment skip_space(freeSpace->position, skip_size);
764+
Segment* const skip_space = FB_NEW_POOL(pool) Segment(freeSpace->position, skip_size);
675765

676766
freeSpace->position += skip_size;
677767
freeSpace->size -= skip_size;
678768
fb_assert(freeSpace->size != 0);
679769

680770
if (!freeSegments.add(skip_space))
681771
fb_assert(false);
772+
freeSegmentsBySize.addSegment(skip_space);
682773

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

686-
freeSpace = &freeSegments.current();
777+
freeSpace = freeSegments.current();
687778
}
688779

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

698789
if (!freeSpace->size)
699790
{
791+
delete freeSegments.current();
700792
is_positioned = freeSegments.fastRemove();
701793
}
794+
else
795+
freeSegmentsBySize.addSegment(freeSpace);
702796
}
703797
else
704798
{

src/jrd/TempSpace.h

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,19 +182,37 @@ class TempSpace : public Firebird::File
182182
class Segment
183183
{
184184
public:
185-
Segment() : position(0), size(0)
186-
{}
187-
188185
Segment(offset_t aPosition, offset_t aSize) :
189-
position(aPosition), size(aSize)
186+
position(aPosition), size(aSize), prev(nullptr), next(nullptr)
190187
{}
191188

192189
offset_t position;
193190
offset_t size;
191+
Segment* prev;
192+
Segment* next;
193+
194+
static const offset_t& generate(const void* /*sender*/, const Segment* segment)
195+
{
196+
return segment->position;
197+
}
198+
};
199+
200+
class SegmentsStack
201+
{
202+
public:
203+
SegmentsStack() : size(0), tail(nullptr)
204+
{}
205+
206+
SegmentsStack(offset_t aSize, Segment* aSegment) :
207+
size(aSize), tail(aSegment)
208+
{}
209+
210+
offset_t size;
211+
Segment* tail;
194212

195-
static const offset_t& generate(const void* /*sender*/, const Segment& segment)
213+
static const offset_t& generate(const void* /*sender*/, const SegmentsStack& segment)
196214
{
197-
return segment.position;
215+
return segment.size;
198216
}
199217
};
200218

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

212-
typedef Firebird::BePlusTree<Segment, offset_t, Segment> FreeSegmentTree;
230+
typedef Firebird::BePlusTree<Segment*, offset_t, Segment> FreeSegmentTree;
231+
typedef Firebird::BePlusTree<SegmentsStack, offset_t, SegmentsStack> FreeSegmentsStackTree;
232+
233+
class FreeSegmentBySize
234+
{
235+
friend bool TempSpace::validate(offset_t& freeSize) const;
236+
237+
public:
238+
FreeSegmentBySize(MemoryPool& pool)
239+
: m_items(pool)
240+
{}
241+
242+
void addSegment(Segment* const segment);
243+
void removeSegment(Segment* const segment);
244+
Segment* getSegment(FB_SIZE_T size);
245+
246+
private:
247+
FreeSegmentsStackTree m_items;
248+
};
249+
213250
FreeSegmentTree freeSegments;
251+
FreeSegmentBySize freeSegmentsBySize;
214252

215253
static Firebird::GlobalPtr<Firebird::Mutex> initMutex;
216254
static Firebird::TempDirectoryList* tempDirs;

0 commit comments

Comments
 (0)