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
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:
commit
85716f78cd
@ -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())
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user