diff --git a/FEXCore/Source/Interface/Config/Config.json.in b/FEXCore/Source/Interface/Config/Config.json.in index d597ec3fb..db567f5bb 100644 --- a/FEXCore/Source/Interface/Config/Config.json.in +++ b/FEXCore/Source/Interface/Config/Config.json.in @@ -221,6 +221,14 @@ "Desc": [ "Optional list of extra read-only disk cache DBs to consider" ] + }, + "DiskCacheMaxFileSize": { + "Type": "uint64", + "Default": "1073741824", + "AffectsCodeGen": "false", + "Desc": [ + "Size limit on the main cache file - index not included. Default 1G" + ] } }, "Emulation": { diff --git a/FEXCore/Source/Interface/Core/DiskCache.cpp b/FEXCore/Source/Interface/Core/DiskCache.cpp index fcf3f8478..d035a3554 100644 --- a/FEXCore/Source/Interface/Core/DiskCache.cpp +++ b/FEXCore/Source/Interface/Core/DiskCache.cpp @@ -276,6 +276,11 @@ namespace DiskCache { return false; } + if (MaxSizeReached || CacheFileSize + Blob.size() >= MaxFileSize) { + MaxSizeReached = true; + return false; + } + if (!CacheFOZ.Lock(STORE_LOCK_TIMEOUT_MS) || !IndexFOZ.Lock(STORE_LOCK_TIMEOUT_MS)) { CacheFOZ.Unlock(); IndexFOZ.Unlock(); @@ -619,6 +624,12 @@ namespace DiskCache { } Advance = true; + + // entry not backed by anything right now - todo prune.. + if (!EntryUnderReview->DB && !BlobRef) { + continue; + } + // do we have enough room in our live code to even hash GuestSize worth? if (Available < EntryUnderReview->GuestSize) { continue; @@ -649,7 +660,7 @@ namespace DiskCache { break; } else if (Validation) { fextl::vector GuestCode(Entry.GuestSize); - if (Entry.Size >= Entry.GuestSize && Entry.DB->ReadCacheBlob(Entry.Offset + Entry.Size - Entry.GuestSize, GuestCode)) { + if (Entry.Size >= Entry.GuestSize && Entry.DB && Entry.DB->ReadCacheBlob(Entry.Offset + Entry.Size - Entry.GuestSize, GuestCode)) { const uint8_t* CachedGuest = GuestCode.data(); const uint8_t* LiveGuest = reinterpret_cast(GuestRIP); uint64_t DiffCount = 0; @@ -857,53 +868,55 @@ namespace DiskCache { uint64_t LookupKey; std::span Blob; fextl::vector IndexBlob; + bool StoreDisk; CacheStoreWorkItem(DiskCache* Self, IndexedDB* DB, const MesaFOZ::foz_payload_key& UniqueKey, uint64_t LookupKey, - std::span Blob, fextl::vector&& IndexBlob) + std::span Blob, fextl::vector&& IndexBlob, bool StoreDisk) : Self(Self) , DB(DB) , UniqueKey(UniqueKey) , LookupKey(LookupKey) , Blob(Blob) - , IndexBlob(std::move(IndexBlob)) {} + , IndexBlob(std::move(IndexBlob)) + , StoreDisk(StoreDisk) {} void Run() override { struct MesaFOZ::mesa_index_db_file_entry IndexHeader; - bool Success = DB->StoreCacheBlob(UniqueKey, LookupKey, Blob, IndexHeader, IndexBlob); + bool DiskSuccess = !StoreDisk || DB->StoreCacheBlob(UniqueKey, LookupKey, Blob, IndexHeader, IndexBlob); - if (Success) { - bool KeepEntryInMemory = true; - // todo possible other lru condition here like entry size? - if (Blob.size() > Self->MemoryLRUMaxSize) { - KeepEntryInMemory = false; - } else { - Self->MemoryLRUCurrentSize += Blob.size(); - } + bool KeepEntryInMemory = true; + // todo possible other lru condition here like entry size? + if (Blob.size() > Self->MemoryLRUMaxSize) { + KeepEntryInMemory = false; + } else { + Self->MemoryLRUCurrentSize += Blob.size(); + } - const IndexExtraBlobHeader* IndexAfterHeader = reinterpret_cast(IndexBlob.data()); + const IndexExtraBlobHeader* IndexAfterHeader = reinterpret_cast(IndexBlob.data()); - fextl::list::iterator NewLRUEntry; - if (KeepEntryInMemory) { - std::lock_guard Guard(Self->MemoryLRULock); - Self->MemoryLRU.push_front({LookupKey, IndexAfterHeader->GuestHash, IndexAfterHeader->GuestFootprint, (uint32_t)Blob.size()}); - NewLRUEntry = Self->MemoryLRU.begin(); - } - { - std::lock_guard Guard(Self->IndexLock); - auto IndexEntry = Self->LookupLocked(LookupKey, IndexAfterHeader->GuestHash, IndexAfterHeader->GuestFootprint); - LOGMAN_THROW_A_FMT(IndexEntry != nullptr, "Stored Index entry not found?"); - if (IndexEntry) { + fextl::list::iterator NewLRUEntry; + if (KeepEntryInMemory) { + std::lock_guard Guard(Self->MemoryLRULock); + Self->MemoryLRU.push_front({LookupKey, IndexAfterHeader->GuestHash, IndexAfterHeader->GuestFootprint, (uint32_t)Blob.size()}); + NewLRUEntry = Self->MemoryLRU.begin(); + } + { + std::lock_guard Guard(Self->IndexLock); + auto IndexEntry = Self->LookupLocked(LookupKey, IndexAfterHeader->GuestHash, IndexAfterHeader->GuestFootprint); + LOGMAN_THROW_A_FMT(IndexEntry != nullptr, "Stored Index entry not found?"); + if (IndexEntry) { + if (StoreDisk && DiskSuccess) { IndexEntry->DB = DB; IndexEntry->Offset = IndexHeader.cache_db_file_offset; - if (!KeepEntryInMemory) { - IndexEntry->MemoryBlob.reset(); - } else { - IndexEntry->LRUEntry = NewLRUEntry; - } + } + if (!KeepEntryInMemory) { + IndexEntry->MemoryBlob.reset(); + } else { + IndexEntry->LRUEntry = NewLRUEntry; } } + } - if (KeepEntryInMemory && Self->MemoryLRUCurrentSize > Self->MemoryLRUMaxSize + Self->MemoryLRUEvictThreshold) { - Self->Writer->QueueWork(fextl::make_unique(Self)); - } + if (KeepEntryInMemory && Self->MemoryLRUCurrentSize > Self->MemoryLRUMaxSize + Self->MemoryLRUEvictThreshold) { + Self->Writer->QueueWork(fextl::make_unique(Self)); } } }; @@ -1173,9 +1186,15 @@ namespace DiskCache { memcpy(IndexBlob.data(), &IndexBlobHeader, sizeof(IndexExtraBlobHeader)); memcpy(IndexBlob.data() + sizeof(IndexExtraBlobHeader), ExactGuestCodeExtents.data(), ExactGuestCodeExtents.size() * sizeof(uint32_t)); + bool StoreDisk = true; + + if (RWCacheDB->Full()) { + StoreDisk = false; + } + // hand the rest off to the writer thread - Writer->QueueWork( - fextl::make_unique(this, RWCacheDB.get(), Key, LookupKey, std::span(BlobData, TotalSize), std::move(IndexBlob))); + Writer->QueueWork(fextl::make_unique(this, RWCacheDB.get(), Key, LookupKey, std::span(BlobData, TotalSize), + std::move(IndexBlob), StoreDisk)); return true; } diff --git a/FEXCore/Source/Interface/Core/DiskCache.h b/FEXCore/Source/Interface/Core/DiskCache.h index 3784e4768..78bedfdd1 100644 --- a/FEXCore/Source/Interface/Core/DiskCache.h +++ b/FEXCore/Source/Interface/Core/DiskCache.h @@ -168,18 +168,23 @@ namespace DiskCache { bool ReadCacheBlob(uint64_t Offset, std::span OutBlob); bool StoreCacheBlob(const MesaFOZ::foz_payload_key& UniqueKey, uint64_t LookupKey, std::span Blob, MesaFOZ::mesa_index_db_file_entry& IndexEntry, std::span IndexBlob); + bool Full() const { + return MaxSizeReached; + } private: // stores run on the Writer, so returning quick isn't as important static constexpr uint32_t STORE_LOCK_TIMEOUT_MS = 1000; static constexpr uint64_t BIG_MAPPING_SIZE = 1ULL << 33; - static constexpr uint32_t LOOKUP_KEY_MAX_BUCKET_DEPTH = 20; FOZFile CacheFOZ; uint8_t* CacheFileMapping = nullptr; std::atomic CacheFileSize; FOZFile IndexFOZ; bool ReadOnly = false; + bool MaxSizeReached = false; + + FEX_CONFIG_OPT(MaxFileSize, DISKCACHEMAXFILESIZE); }; class DiskCache { @@ -246,7 +251,7 @@ namespace DiskCache { // Be aware of the impact of changing this frequently! static constexpr uint16_t FormatVersion = 26; - static constexpr uint32_t LOOKUP_KEY_MAX_BUCKET_DEPTH = 20; + static constexpr uint32_t LOOKUP_KEY_MAX_BUCKET_DEPTH = 500; } // namespace DiskCache