From b2eb581b2fc73378ea57c3de6f20fe4434be3178 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Fri, 1 Sep 2023 10:53:47 +0300 Subject: [PATCH 1/2] Per-attachment cache of often used BDB's --- builds/win32/msvc15/engine_static.vcxproj | 1 + .../msvc15/engine_static.vcxproj.filters | 3 +- src/jrd/Attachment.h | 3 + src/jrd/PageToBufferMap.h | 217 ++++++++++++++++++ src/jrd/cch.cpp | 45 ++++ 5 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 src/jrd/PageToBufferMap.h diff --git a/builds/win32/msvc15/engine_static.vcxproj b/builds/win32/msvc15/engine_static.vcxproj index 6d6785ff44..a8bf00b46a 100644 --- a/builds/win32/msvc15/engine_static.vcxproj +++ b/builds/win32/msvc15/engine_static.vcxproj @@ -310,6 +310,7 @@ + diff --git a/builds/win32/msvc15/engine_static.vcxproj.filters b/builds/win32/msvc15/engine_static.vcxproj.filters index 36d1aca2ef..861d5f0b4d 100644 --- a/builds/win32/msvc15/engine_static.vcxproj.filters +++ b/builds/win32/msvc15/engine_static.vcxproj.filters @@ -1085,6 +1085,7 @@ Header files + @@ -1130,4 +1131,4 @@ DSQL - + \ No newline at end of file diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 7a6678eaef..b00308a427 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -73,6 +73,7 @@ namespace Jrd class jrd_file; class Format; class BufferControl; + class PageToBufferMap; class SparseBitmap; class jrd_rel; class ExternalFile; @@ -639,6 +640,8 @@ public: USHORT att_current_timezone; int att_parallel_workers; + PageToBufferMap* att_bdb_cache; // managed in CCH, created in att_pool, freed with it + Firebird::RefPtr att_replicator; Firebird::AutoPtr att_repl_matcher; Firebird::Array att_repl_appliers; diff --git a/src/jrd/PageToBufferMap.h b/src/jrd/PageToBufferMap.h new file mode 100644 index 0000000000..b5440512a9 --- /dev/null +++ b/src/jrd/PageToBufferMap.h @@ -0,0 +1,217 @@ +/* + * PROGRAM: JRD Access Method + * MODULE: PageToBufferMap.h + * DESCRIPTION: Support of disk cache manager + * + * The contents of this file are subject to the Interbase Public + * License Version 1.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy + * of the License at http://www.Inprise.com/IPL.html + * + * Software distributed under the License is distributed on an + * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express + * or implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code was created by Vladyslav Khorsun for the + * Firebird Open Source RDBMS project. + * + * Copyright (c) 2023 Vladyslav Khorsun + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#ifndef JRD_PAGE_TO_BUFFER_MAP +#define JRD_PAGE_TO_BUFFER_MAP + +#include "../common/classes/Hash.h" +#include "../jrd/cch.h" + +namespace Jrd { + +// PageToBufferMap used to cache pointers to the often used page buffers. +// Its purpose is to avoid more costly usage of shared hash table. + +class PageToBufferMap +{ +public: + static const size_t MAP_SIZE = 64; + + PageToBufferMap(MemoryPool& pool) : + m_map(pool) + { + Item* items = FB_NEW_POOL(pool) Item[MAP_SIZE]; + for (FB_SIZE_T i = 1; i < MAP_SIZE; i++) + items[i - 1].m_next = &items[i]; + + m_free = items; + } + + BufferDesc* get(PageNumber page) + { + Item* item = m_map.lookup(page); + + if (!item) + return nullptr; + + if (m_list != item) + { + listRemove(item); + listInsert(item); + } + + return item->m_bdb; + } + + void put(BufferDesc* bdb) + { + Item* item = m_map.lookup(bdb->bdb_page); + if (item) + { + if (m_list != item) + listRemove(item); + } + else + { + item = getFreeItem(); + item->m_page = bdb->bdb_page; + m_map.add(item); + } + + item->m_bdb = bdb; + if (m_list != item) + listInsert(item); + } + + void remove(PageNumber page) + { + Item* item = m_map.remove(page); + + fb_assert(item); + if (!item) + return; + + listRemove(item); + + item->m_bdb = nullptr; + item->m_next = m_free; + m_free = item; + } + +private: + struct Item; + using HashTableType = Firebird::HashTable; + + struct Item : public HashTableType::Entry + { + PageNumber m_page; + BufferDesc* m_bdb = nullptr; + Item* m_next = nullptr; // LRU list support + Item* m_prev = nullptr; + + // KeyOfValue + static PageNumber generate(Item& item) + { + return item.m_page; + } + + // Hash function + static FB_SIZE_T hash(const PageNumber& value, FB_SIZE_T hashSize) + { + return value.getPageNum() % hashSize; + } + + // HashTable::Entry + bool isEqual(const PageNumber& page) const override + { + return this->m_page == page; + } + + Item* get() override + { + return this; + } + }; + + Item* getFreeItem() + { + Item* item = m_free; + if (item) + { + m_free = m_free->m_next; + item->m_next = nullptr; + return item; + } + + // get least recently used item from list + fb_assert(m_list != nullptr); + + item = m_list->m_prev; + listRemove(item); + + if (m_map.remove(item->m_page) != item) + fb_assert(false); + + item->m_bdb = nullptr; + return item; + } + + + void listRemove(Item* item) + { + // remove item from list + fb_assert(m_list != nullptr); + fb_assert(item->m_next); + fb_assert(item->m_prev); + + if (item->m_next == item) + { + fb_assert(item->m_prev == item); + fb_assert(m_list == item); + m_list = nullptr; + } + else + { + if (m_list == item) + m_list = item->m_next; + + item->m_next->m_prev = item->m_prev; + item->m_prev->m_next = item->m_next; + } + + item->m_next = item->m_prev = nullptr; + } + + void listInsert(Item* item) + { + // put item at the list head + if (m_list != nullptr) + { + fb_assert(m_list->m_next); + fb_assert(m_list->m_prev); + + item->m_next = m_list; + item->m_prev = m_list->m_prev; + + item->m_next->m_prev = item; + item->m_prev->m_next = item; + } + else + { + item->m_next = item; + item->m_prev = item; + } + + m_list = item; + } + + HashTableType m_map; + Item* m_list = nullptr; // head of LRU list + Item* m_free = nullptr; // unused items +}; + +} // namespace Jrd + +#endif // JRD_PAGE_TO_BUFFER_MAP diff --git a/src/jrd/cch.cpp b/src/jrd/cch.cpp index 5b4a355151..836c55b34b 100644 --- a/src/jrd/cch.cpp +++ b/src/jrd/cch.cpp @@ -62,6 +62,7 @@ #include "../common/classes/MsgPrint.h" #include "../jrd/CryptoManager.h" #include "../common/utils_proto.h" +#include "../jrd/PageToBufferMap.h" // Use lock-free lists in hash table implementation #define HASH_USE_CDS_LIST @@ -131,6 +132,7 @@ static void prefetch_init(Prefetch*, thread_db*); static void prefetch_io(Prefetch*, FbStatusVector *); static void prefetch_prologue(Prefetch*, SLONG *); #endif +static void cacheBuffer(Attachment* att, BufferDesc* bdb); static void check_precedence(thread_db*, WIN*, PageNumber); static void clear_precedence(thread_db*, BufferDesc*); static void down_grade(thread_db*, BufferDesc*, int high = 0); @@ -3152,6 +3154,18 @@ void BufferControl::exceptionHandler(const Firebird::Exception& ex, BcbThreadSyn } +static void cacheBuffer(Attachment* att, BufferDesc* bdb) +{ + if (att) + { + if (!att->att_bdb_cache) + att->att_bdb_cache = FB_NEW_POOL(*att->att_pool) PageToBufferMap(*att->att_pool); + + att->att_bdb_cache->put(bdb); + } +} + + static void check_precedence(thread_db* tdbb, WIN* window, PageNumber page) { /************************************** @@ -3782,6 +3796,33 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s SET_TDBB(tdbb); Database* dbb = tdbb->getDatabase(); BufferControl* bcb = dbb->dbb_bcb; + Attachment* att = tdbb->getAttachment(); + + if (att && att->att_bdb_cache) + { + BufferDesc* bdb = att->att_bdb_cache->get(page); + if (bdb) + { + if (bdb->addRef(tdbb, syncType, wait)) + { + if (bdb->bdb_page == page) + { + recentlyUsed(bdb); + tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES); + return bdb; + } + + bdb->release(tdbb, true); + att->att_bdb_cache->remove(page); + } + else + { + fb_assert(wait <= 0); + if (bdb->bdb_page == page) + return nullptr; + } + } + } while (true) { @@ -3811,6 +3852,7 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s { recentlyUsed(bdb); tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES); + cacheBuffer(att, bdb); return bdb; } @@ -3850,6 +3892,7 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s bdb->downgrade(syncType); recentlyUsed(bdb); tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES); + cacheBuffer(att, bdb); return bdb; } } @@ -3893,6 +3936,7 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s recentlyUsed(bdb); } tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES); + cacheBuffer(att, bdb); return bdb; } } @@ -3910,6 +3954,7 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s } recentlyUsed(bdb2); tdbb->bumpStats(RuntimeStatistics::PAGE_FETCHES); + cacheBuffer(att, bdb2); } else bdb2 = nullptr; From 469715dbf903263ebc52b306df69379feeab1daa Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Sat, 2 Sep 2023 12:29:08 +0300 Subject: [PATCH 2/2] Misc changes per @dyemanov requests --- src/jrd/PageToBufferMap.h | 4 ++-- src/jrd/cch.cpp | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/jrd/PageToBufferMap.h b/src/jrd/PageToBufferMap.h index b5440512a9..f15a878802 100644 --- a/src/jrd/PageToBufferMap.h +++ b/src/jrd/PageToBufferMap.h @@ -36,10 +36,10 @@ namespace Jrd { class PageToBufferMap { -public: static const size_t MAP_SIZE = 64; - PageToBufferMap(MemoryPool& pool) : +public: + explicit PageToBufferMap(MemoryPool& pool) : m_map(pool) { Item* items = FB_NEW_POOL(pool) Item[MAP_SIZE]; diff --git a/src/jrd/cch.cpp b/src/jrd/cch.cpp index 836c55b34b..6b324931f2 100644 --- a/src/jrd/cch.cpp +++ b/src/jrd/cch.cpp @@ -3800,8 +3800,7 @@ static BufferDesc* get_buffer(thread_db* tdbb, const PageNumber page, SyncType s if (att && att->att_bdb_cache) { - BufferDesc* bdb = att->att_bdb_cache->get(page); - if (bdb) + if (BufferDesc* bdb = att->att_bdb_cache->get(page)) { if (bdb->addRef(tdbb, syncType, wait)) {