Skip to content

Cache sort blocks by the sort owners to avoid contention in shared cache of sorts blocks. #7671

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 10, 2023
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
4 changes: 2 additions & 2 deletions src/jrd/idx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,16 +214,16 @@ class IndexCreateTask : public Task

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())
Expand Down
2 changes: 1 addition & 1 deletion src/jrd/req.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ class Request : public pool_alloc<type_req>
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)
{
Expand Down
89 changes: 59 additions & 30 deletions src/jrd/sort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
/**************************************
*
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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&)
{
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}


Expand Down Expand Up @@ -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


Expand Down
9 changes: 7 additions & 2 deletions src/jrd/sort.h
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,8 @@ class PartitionedSort
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()
Expand Down Expand Up @@ -426,9 +426,14 @@ class SortOwner
return pool;
}

UCHAR* allocateBuffer();
void releaseBuffer(UCHAR*);

private:
MemoryPool& pool;
Database* const dbb;
Firebird::SortedArray<Sort*> sorts;
Firebird::HalfStaticArray<UCHAR*, 4> buffers;
};

} //namespace Jrd
Expand Down
3 changes: 1 addition & 2 deletions src/jrd/tra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/jrd/tra.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class jrd_tra : public pool_alloc<type_tra>
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),
Expand Down