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:
parent
244e4be522
commit
d5f73f8018
@ -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
|
||||
|
147
src/jrd/cch.cpp
147
src/jrd/cch.cpp
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user