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

Merge pull request #7671 from FirebirdSQL/work/SortBlocksCache
All checks were successful
continuous-integration/drone/push Build is passing

Cache sort blocks by the sort owners to avoid contention in shared cache of sorts blocks.
This commit is contained in:
Vlad Khorsun 2023-07-11 01:20:58 +03:00 committed by GitHub
commit 85716f78cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 71 additions and 38 deletions

View File

@ -214,16 +214,16 @@ public:
IndexCreateTask(thread_db* tdbb, MemoryPool* pool, IndexCreation* creation) : Task(),
m_pool(pool),
m_dbb(tdbb->getDatabase()),
m_tdbb_flags(tdbb->tdbb_flags),
m_flags(0),
m_creation(creation),
m_sorts(*m_pool),
m_sorts(*m_pool, m_dbb),
m_items(*m_pool),
m_stop(false),
m_countPP(0),
m_nextPP(0)
{
m_dbb = tdbb->getDatabase();
Attachment* att = tdbb->getAttachment();
if (att->isGbak())

View File

@ -316,7 +316,7 @@ public:
req_timeout(0),
req_domain_validation(NULL),
req_auto_trans(*req_pool),
req_sorts(*req_pool),
req_sorts(*req_pool, attachment->att_database),
req_rpb(*req_pool),
impureArea(*req_pool)
{

View File

@ -68,12 +68,6 @@ const USHORT MAX_MERGE_LEVEL = 2;
using namespace Jrd;
using namespace Firebird;
void SortOwner::unlinkAll()
{
while (sorts.getCount())
delete sorts.pop();
}
// The sort buffer size should be just under a multiple of the
// hardware memory page size to account for memory allocator
// overhead. On most platforms, this saves 4KB to 8KB per sort
@ -165,10 +159,11 @@ Sort::Sort(Database* dbb,
FPTR_REJECT_DUP_CALLBACK call_back,
void* user_arg,
FB_UINT64 max_records)
: m_dbb(dbb), m_last_record(NULL), m_next_pointer(NULL), m_records(0),
: m_dbb(dbb), m_owner(owner),
m_last_record(NULL), m_next_pointer(NULL), m_records(0),
m_runs(NULL), m_merge(NULL), m_free_runs(NULL),
m_flags(0), m_merge_pool(NULL),
m_description(owner->getPool(), keys)
m_description(m_owner->getPool(), keys)
{
/**************************************
*
@ -185,7 +180,7 @@ Sort::Sort(Database* dbb,
* includes index key (which must be unique) and record numbers.
*
**************************************/
fb_assert(owner);
fb_assert(m_owner);
fb_assert(unique_keys <= keys);
try
@ -194,7 +189,7 @@ Sort::Sort(Database* dbb,
// key description vector. Round the record length up to the next
// longword, and add a longword to a pointer back to the pointer slot.
MemoryPool& pool = owner->getPool();
MemoryPool& pool = m_owner->getPool();
const ULONG record_size = ROUNDUP(record_length + SIZEOF_SR_BCKPTR, FB_ALIGNMENT);
m_longs = record_size >> SHIFTLONG;
@ -248,8 +243,7 @@ Sort::Sort(Database* dbb,
// Link in new sort block
m_owner = owner;
owner->linkSort(this);
m_owner->linkSort(this);
}
catch (const BadAlloc&)
{
@ -610,15 +604,12 @@ void Sort::sort(thread_db* tdbb)
void Sort::allocateBuffer(MemoryPool& pool)
{
if (m_dbb->dbb_sort_buffers.hasData() && m_max_alloc_size <= MAX_SORT_BUFFER_SIZE)
if (m_max_alloc_size <= MAX_SORT_BUFFER_SIZE)
{
SyncLockGuard guard(&m_dbb->dbb_sortbuf_sync, SYNC_EXCLUSIVE, "Sort::allocateBuffer");
if (m_dbb->dbb_sort_buffers.hasData())
m_memory = m_owner->allocateBuffer();
if (m_memory)
{
// The sort buffer cache has at least one big block, let's use it
m_size_memory = MAX_SORT_BUFFER_SIZE;
m_memory = m_dbb->dbb_sort_buffers.pop();
m_flags |= scb_reuse_buffer;
return;
}
@ -666,23 +657,14 @@ void Sort::allocateBuffer(MemoryPool& pool)
void Sort::releaseBuffer()
{
// Here we cache blocks to be reused later, but only the biggest ones
const size_t MAX_CACHED_SORT_BUFFERS = 8; // 1MB
SyncLockGuard guard(&m_dbb->dbb_sortbuf_sync, SYNC_EXCLUSIVE, "Sort::releaseBuffer");
if ((m_flags & scb_reuse_buffer) &&
m_dbb->dbb_sort_buffers.getCount() < MAX_CACHED_SORT_BUFFERS)
if (m_flags & scb_reuse_buffer)
{
fb_assert(m_size_memory == MAX_SORT_BUFFER_SIZE);
m_dbb->dbb_sort_buffers.push(m_memory);
m_flags &= ~scb_reuse_buffer;
m_owner->releaseBuffer(m_memory);
}
else
delete[] m_memory;
m_flags &= ~scb_reuse_buffer;
}
@ -2181,6 +2163,53 @@ void Sort::sortRunsBySeek(int n)
}
/// class SortOwner
UCHAR* SortOwner::allocateBuffer()
{
if (buffers.hasData())
return buffers.pop();
if (dbb->dbb_sort_buffers.hasData())
{
SyncLockGuard guard(&dbb->dbb_sortbuf_sync, SYNC_EXCLUSIVE, FB_FUNCTION);
// The sort buffer cache has at least one big block, let's use it
if (dbb->dbb_sort_buffers.hasData())
return dbb->dbb_sort_buffers.pop();
}
return nullptr;
}
void SortOwner::releaseBuffer(UCHAR* memory)
{
buffers.push(memory);
}
void SortOwner::unlinkAll()
{
while (sorts.getCount())
delete sorts.pop();
if (buffers.hasData())
{
// Move cached buffers to the database level cache to be reused later by other attachments
const size_t MAX_CACHED_SORT_BUFFERS = 8; // 1MB
SyncLockGuard guard(&dbb->dbb_sortbuf_sync, SYNC_EXCLUSIVE, FB_FUNCTION);
while (buffers.hasData() && dbb->dbb_sort_buffers.getCount() < MAX_CACHED_SORT_BUFFERS)
dbb->dbb_sort_buffers.push(buffers.pop());
}
while (buffers.hasData())
delete[] buffers.pop();
}
/// class PartitionedSort

View File

@ -389,8 +389,8 @@ private:
class SortOwner
{
public:
explicit SortOwner(MemoryPool& p)
: pool(p), sorts(p)
SortOwner(MemoryPool& p, Database* database)
: pool(p), dbb(database), sorts(p), buffers(p)
{}
~SortOwner()
@ -426,9 +426,14 @@ public:
return pool;
}
UCHAR* allocateBuffer();
void releaseBuffer(UCHAR*);
private:
MemoryPool& pool;
Database* const dbb;
Firebird::SortedArray<Sort*> sorts;
Firebird::HalfStaticArray<UCHAR*, 4> buffers;
};
} //namespace Jrd

View File

@ -813,8 +813,7 @@ void TRA_init(Jrd::Attachment* attachment)
CHECK_DBB(dbb);
MemoryPool* const pool = dbb->dbb_permanent;
jrd_tra* const trans = FB_NEW_POOL(*pool) jrd_tra(pool, &dbb->dbb_memory_stats, NULL, NULL);
trans->tra_attachment = attachment;
jrd_tra* const trans = FB_NEW_POOL(*pool) jrd_tra(pool, &dbb->dbb_memory_stats, attachment, NULL);
attachment->setSysTransaction(trans);
trans->tra_flags |= TRA_system | TRA_ignore_limbo;
}

View File

@ -187,7 +187,7 @@ public:
tra_outer(outer),
tra_snapshot_handle(0),
tra_snapshot_number(0),
tra_sorts(*p),
tra_sorts(*p, attachment->att_database),
tra_gen_ids(NULL),
tra_replicator(NULL),
tra_interface(NULL),