8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 10:03:03 +01:00

Improvement CORE-5660 : Make flash of big number of dirty pages faster

This commit is contained in:
hvlad 2017-11-14 16:27:56 +02:00
parent 244e4be522
commit d5f73f8018
2 changed files with 186 additions and 80 deletions

View File

@ -219,6 +219,125 @@ public:
}
};
// Templates to allow to iterate thru array\vector of values and process items
// in some way. Processed items are marked and skipped at next iteration circle.
// Idea is to not remove processed items from array and avoid costly memory
// moving. Also, iterator is able to move upper and lower bounds of array if
// last (or first) items are marked, making next iterations more efficient.
template <typename T>
class DefaultMarkValue
{
public:
static T getMarkValue()
{
return T(0);
}
};
template <typename T, typename MarkValue = DefaultMarkValue<T> >
class DefaultMarker
{
public:
static void mark(T* const item)
{
*item = MarkValue::getMarkValue();
};
static bool isMarked(const T* const item)
{
return *item == MarkValue::getMarkValue();
};
};
template <typename T, typename Marker = DefaultMarker<T> >
class MarkIterator
{
public:
MarkIterator(T* begin, FB_SIZE_T count) :
m_begin(begin),
m_end(begin + count),
m_curr(begin),
m_last(begin)
{
}
// Mark current item as processed
void mark()
{
Marker::mark(m_curr);
if (m_last == m_curr)
m_last--;
}
// Move iterator position to the first not processed item
void rewind()
{
m_curr = m_begin;
m_end = m_last + 1;
m_last = m_begin;
}
T& operator*() const
{
fb_assert(m_begin <= m_curr);
fb_assert(m_curr < m_end);
return *m_curr;
}
// Advance iterator to the next not processed item, if exist
void operator++()
{
fb_assert(m_begin <= m_curr);
fb_assert(m_curr < m_end);
const bool at_begin = (m_begin == m_curr);
m_curr++;
while (Marker::isMarked(m_curr) && m_curr < m_end)
m_curr++;
if (m_curr == m_end)
return;
if (at_begin)
{
if (Marker::isMarked(m_begin))
m_begin = m_curr;
else if (m_begin != m_curr - 1)
{
m_curr[-1] = *m_begin;
m_begin = m_curr - 1;
}
}
if (!Marker::isMarked(m_curr))
m_last = m_curr;
return;
}
// Show if current position is valid
bool isEof() const
{
return m_curr >= m_end;
}
// Show if not processed items still exists
bool isEmpty() const
{
return (m_begin >= m_end);
}
private:
T* m_begin;
T* m_end;
T* m_curr;
T* m_last;
};
} // namespace Firebird
#endif

View File

@ -184,6 +184,7 @@ static inline void removeDirty(BufferControl* bcb, BufferDesc* bdb)
static void flushDirty(thread_db* tdbb, SLONG transaction_mask, const bool sys_only);
static void flushAll(thread_db* tdbb, USHORT flush_flag);
static void flushPages(thread_db* tdbb, USHORT flush_flag, BufferDesc** begin, FB_SIZE_T count);
static void recentlyUsed(BufferDesc* bdb);
static void requeueRecentlyUsed(BufferControl* bcb);
@ -2475,24 +2476,6 @@ static int blocking_ast_bdb(void* ast_object)
}
// Used in qsort below
extern "C" {
static int cmpBdbs(const void* a, const void* b)
{
const BufferDesc* bdbA = *(BufferDesc**) a;
const BufferDesc* bdbB = *(BufferDesc**) b;
if (bdbA->bdb_page > bdbB->bdb_page)
return 1;
if (bdbA->bdb_page < bdbB->bdb_page)
return -1;
return 0;
}
} // extern C
// Remove cleared precedence blocks from high precedence queue
static void purgePrecedence(BufferControl* bcb, BufferDesc* bdb)
{
@ -2515,18 +2498,11 @@ static void purgePrecedence(BufferControl* bcb, BufferDesc* bdb)
}
}
// Write pages modified by given or system transaction to disk. First sort all
// corresponding pages by their numbers to make writes physically ordered and
// thus faster. At every iteration of while loop write pages which have no high
// precedence pages to ensure order preserved. If after some iteration there are
// no such pages (i.e. all of not written yet pages have high precedence pages)
// then write them all at last iteration (of course write_buffer will also check
// for precedence before write)
// Collect pages modified by given or system transaction and write it to disk.
// See also comments in flushPages.
static void flushDirty(thread_db* tdbb, SLONG transaction_mask, const bool sys_only)
{
SET_TDBB(tdbb);
FbStatusVector* const status = tdbb->tdbb_status_vector;
Database* dbb = tdbb->getDatabase();
BufferControl* bcb = dbb->dbb_bcb;
Firebird::HalfStaticArray<BufferDesc*, 1024> flush;
@ -2557,56 +2533,18 @@ static void flushDirty(thread_db* tdbb, SLONG transaction_mask, const bool sys_o
}
}
qsort(flush.begin(), flush.getCount(), sizeof(BufferDesc*), cmpBdbs);
bool writeAll = false;
while (flush.getCount())
{
BufferDesc** ptr = flush.begin();
const size_t cnt = flush.getCount();
while (ptr < flush.end())
{
BufferDesc* bdb = *ptr;
bdb->addRef(tdbb, SYNC_SHARED);
if (!writeAll)
purgePrecedence(bcb, bdb);
if (writeAll || QUE_EMPTY(bdb->bdb_higher))
{
const PageNumber page = bdb->bdb_page;
if (!write_buffer(tdbb, bdb, page, false, status, true))
CCH_unwind(tdbb, true);
// re-post the lock only if it was really written
bdb->release(tdbb, !(bdb->bdb_flags & BDB_dirty));
flush.remove(ptr);
}
else
{
bdb->release(tdbb, false);
ptr++;
}
}
if (cnt == flush.getCount())
writeAll = true;
}
flushPages(tdbb, FLUSH_TRAN, flush.begin(), flush.getCount());
}
// Write pages modified by garbage collector or all dirty pages or release page
// locks - depending of flush_flag. See also comments in flushDirty
// Collect pages modified by garbage collector or all dirty pages or release page
// locks - depending of flush_flag, and write it to disk.
// See also comments in flushPages.
static void flushAll(thread_db* tdbb, USHORT flush_flag)
{
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
BufferControl* bcb = dbb->dbb_bcb;
FbStatusVector* status = tdbb->tdbb_status_vector;
Firebird::HalfStaticArray<BufferDesc*, 1024> flush(bcb->bcb_dirty_count);
const bool all_flag = (flush_flag & FLUSH_ALL) != 0;
@ -2643,18 +2581,62 @@ static void flushAll(thread_db* tdbb, USHORT flush_flag)
}
}
qsort(flush.begin(), flush.getCount(), sizeof(BufferDesc*), cmpBdbs);
flushPages(tdbb, flush_flag, flush.begin(), flush.getCount());
}
bool writeAll = false;
while (flush.getCount())
// Used in qsort below
extern "C" {
static int cmpBdbs(const void* a, const void* b)
{
BufferDesc** ptr = flush.begin();
const size_t cnt = flush.getCount();
while (ptr < flush.end())
const BufferDesc* bdbA = *(BufferDesc**)a;
const BufferDesc* bdbB = *(BufferDesc**)b;
if (bdbA->bdb_page > bdbB->bdb_page)
return 1;
if (bdbA->bdb_page < bdbB->bdb_page)
return -1;
return 0;
}
} // extern C
// Write array of pages to disk in efficient order.
// First, sort pages by their numbers to make writes physically ordered and
// thus faster. At every iteration of while loop write pages which have no high
// precedence pages to ensure order preserved. If after some iteration there are
// no such pages (i.e. all of not written yet pages have high precedence pages)
// then write them all at last iteration (of course write_buffer will also check
// for precedence before write).
static void flushPages(thread_db* tdbb, USHORT flush_flag, BufferDesc** begin, FB_SIZE_T count)
{
FbStatusVector* const status = tdbb->tdbb_status_vector;
const bool all_flag = (flush_flag & FLUSH_ALL) != 0;
const bool release_flag = (flush_flag & FLUSH_RLSE) != 0;
const bool write_thru = release_flag;
qsort(begin, count, sizeof(BufferDesc*), cmpBdbs);
MarkIterator<BufferDesc*> iter(begin, count);
FB_SIZE_T written = 0;
bool writeAll = false;
while (!iter.isEmpty())
{
bool found = false;
for (; !iter.isEof(); ++iter)
{
BufferDesc* bdb = *ptr;
BufferDesc* bdb = *iter;
fb_assert(bdb);
if (!bdb)
continue;
bdb->addRef(tdbb, release_flag ? SYNC_EXCLUSIVE : SYNC_SHARED);
BufferControl* bcb = bdb->bdb_bcb;
if (!writeAll)
purgePrecedence(bcb, bdb);
@ -2666,7 +2648,7 @@ static void flushAll(thread_db* tdbb, USHORT flush_flag)
BUGCHECK(210); // msg 210 page in use during flush
}
if (bdb->bdb_flags & (BDB_db_dirty | BDB_dirty))
if (!all_flag || bdb->bdb_flags & (BDB_db_dirty | BDB_dirty))
{
if (!write_buffer(tdbb, bdb, bdb->bdb_page, write_thru, status, true))
CCH_unwind(tdbb, true);
@ -2678,18 +2660,23 @@ static void flushAll(thread_db* tdbb, USHORT flush_flag)
PAGE_LOCK_RELEASE(tdbb, bcb, bdb->bdb_lock);
bdb->release(tdbb, !release_flag && !(bdb->bdb_flags & BDB_dirty));
flush.remove(ptr);
iter.mark();
found = true;
written++;
}
else
{
bdb->release(tdbb, false);
ptr++;
}
}
if (cnt == flush.getCount())
if (!found)
writeAll = true;
iter.rewind();
}
fb_assert(count == written);
}