diff --git a/dev/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp b/dev/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp index d8a07773ea..fae7baae98 100644 --- a/dev/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp +++ b/dev/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.cpp @@ -13,11 +13,14 @@ #include #include #include +#include namespace AZ { namespace IO { + using RecursiveLockGuard = AZStd::lock_guard; + DedicatedCache::DedicatedCache(u64 cacheSize, u32 blockSize, bool onlyEpilogWrites) : StreamStackEntry("Dedicated cache") , m_cacheSize(cacheSize) @@ -29,6 +32,9 @@ namespace AZ void DedicatedCache::SetNext(AZStd::shared_ptr next) { m_next = AZStd::move(next); + + RecursiveLockGuard lock(m_cacheMutex); + for (AZStd::unique_ptr& cache : m_cachedFileCaches) { cache->SetNext(m_next); @@ -38,6 +44,9 @@ namespace AZ void DedicatedCache::SetContext(StreamerContext& context) { StreamStackEntry::SetContext(context); + + RecursiveLockGuard lock(m_cacheMutex); + for (AZStd::unique_ptr& cache : m_cachedFileCaches) { cache->SetContext(context); @@ -62,10 +71,16 @@ namespace AZ bool DedicatedCache::ExecuteRequests() { bool hasProcessedRequest = false; - for (AZStd::unique_ptr& cache : m_cachedFileCaches) + { - hasProcessedRequest = cache->ExecuteRequests() || hasProcessedRequest; + RecursiveLockGuard lock(m_cacheMutex); + + for (AZStd::unique_ptr& cache : m_cachedFileCaches) + { + hasProcessedRequest = cache->ExecuteRequests() || hasProcessedRequest; + } } + return StreamStackEntry::ExecuteRequests() || hasProcessedRequest; } @@ -73,108 +88,155 @@ namespace AZ { FileRequest::ReadData& data = request->GetReadData(); - size_t index = FindCache(*data.m_path, data.m_offset); - if (index == s_fileNotFound) { - if (m_next) + RecursiveLockGuard lock(m_cacheMutex); + + size_t index = FindCache(*data.m_path, data.m_offset); + if (index != s_fileNotFound) { - m_next->PrepareRequest(request); + m_cachedFileCaches[index]->PrepareRequest(request); + return; } } - else + + if (m_next) { - m_cachedFileCaches[index]->PrepareRequest(request); + m_next->PrepareRequest(request); } } void DedicatedCache::FlushCache(const RequestPath& filePath) { - size_t count = m_cachedFileNames.size(); - for (size_t i = 0; i < count; ++i) { - if (m_cachedFileNames[i] == filePath) + RecursiveLockGuard lock(m_cacheMutex); + + size_t count = m_cachedFileNames.size(); + for (size_t i = 0; i < count; ++i) { - m_cachedFileCaches[i]->FlushCache(filePath); + if (m_cachedFileNames[i] == filePath) + { + m_cachedFileCaches[i]->FlushCache(filePath); + } } } + StreamStackEntry::FlushCache(filePath); } void DedicatedCache::FlushEntireCache() { - for (AZStd::unique_ptr& cache : m_cachedFileCaches) { - cache->FlushEntireCache(); + RecursiveLockGuard lock(m_cacheMutex); + + for (AZStd::unique_ptr& cache : m_cachedFileCaches) + { + cache->FlushEntireCache(); + } } + StreamStackEntry::FlushEntireCache(); } void DedicatedCache::CollectStatistics(AZStd::vector& statistics) const { - size_t count = m_cachedFileNames.size(); - for (size_t i = 0; i < count; ++i) { - double hitRate = m_cachedFileCaches[i]->CalculateHitRatePercentage(); - double cacheable = m_cachedFileCaches[i]->CalculateCacheableRatePercentage(); - s32 slots = m_cachedFileCaches[i]->CalculateAvailableRequestSlots(); - - AZStd::string name; - if (m_cachedFileRanges[i].IsEntireFile()) - { - name = AZStd::string::format("%s/%s", m_name.c_str(), m_cachedFileNames[i].GetRelativePath()); - } - else + RecursiveLockGuard lock(m_cacheMutex); + + DedicatedCache* _this = const_cast(this); + _this->CleanupCachedNames(); + size_t count = m_cachedFileNames.size(); + for (size_t i = 0; i < count; ++i) { - name = AZStd::string::format("%s/%s %llu:%llu", m_name.c_str(), m_cachedFileNames[i].GetRelativePath(), - m_cachedFileRanges[i].GetOffset(), m_cachedFileRanges[i].GetEndPoint()); - } + double hitRate = m_cachedFileCaches[i]->CalculateHitRatePercentage(); + double cacheable = m_cachedFileCaches[i]->CalculateCacheableRatePercentage(); + s32 slots = m_cachedFileCaches[i]->CalculateAvailableRequestSlots(); + + AZStd::string_view name; + + if (m_cachedFileRanges[i].IsEntireFile()) + { + auto combinedName = AZStd::string::format("%s/%s", m_name.c_str(), m_cachedFileNames[i].GetRelativePath()); + name = _this->GetOrAddCachedName(AZStd::hash_string(combinedName.begin(), combinedName.length()), AZStd::move(combinedName)); + } + else + { + auto combinedName = AZStd::string::format("%s/%s %llu:%llu", m_name.c_str(), m_cachedFileNames[i].GetRelativePath(), + m_cachedFileRanges[i].GetOffset(), m_cachedFileRanges[i].GetEndPoint()); + name = _this->GetOrAddCachedName(AZStd::hash_string(combinedName.begin(), combinedName.length()), AZStd::move(combinedName)); + } - statistics.push_back(Statistic::CreatePercentage(name, "Cache Hit Rate", hitRate)); - statistics.push_back(Statistic::CreatePercentage(name, "Cacheable", cacheable)); - statistics.push_back(Statistic::CreateInteger(name, "Available slots", slots)); + statistics.push_back(Statistic::CreatePercentage(name, "Cache Hit Rate", hitRate)); + statistics.push_back(Statistic::CreatePercentage(name, "Cacheable", cacheable)); + statistics.push_back(Statistic::CreateInteger(name, "Available slots", slots)); + } } + StreamStackEntry::CollectStatistics(statistics); } void DedicatedCache::CreateDedicatedCache(const RequestPath& filename, const FileRange& range) { - size_t index = FindCache(filename, range); - if (index == s_fileNotFound) - { - index = m_cachedFileCaches.size(); - m_cachedFileNames.push_back(filename); - m_cachedFileRanges.push_back(range); - m_cachedFileCaches.push_back(AZStd::make_unique(m_cacheSize, m_blockSize, m_onlyEpilogWrites)); - m_cachedFileCaches[index]->SetNext(m_next); - m_cachedFileCaches[index]->SetContext(*m_context); - m_cachedFileRefCounts.push_back(1); - } - else { - ++m_cachedFileRefCounts[index]; + RecursiveLockGuard lock(m_cacheMutex); + + size_t index = FindCache(filename, range); + if (index == s_fileNotFound) + { + index = m_cachedFileCaches.size(); + m_cachedFileNames.push_back(filename); + m_cachedFileRanges.push_back(range); + m_cachedFileCaches.push_back(AZStd::make_unique(m_cacheSize, m_blockSize, m_onlyEpilogWrites)); + m_cachedFileCaches[index]->SetNext(m_next); + m_cachedFileCaches[index]->SetContext(*m_context); + m_cachedFileRefCounts.push_back(1); + } + else + { + ++m_cachedFileRefCounts[index]; + } } + StreamStackEntry::CreateDedicatedCache(filename, range); } void DedicatedCache::DestroyDedicatedCache(const RequestPath& filename, const FileRange& range) { - size_t index = FindCache(filename, range); - if (index != s_fileNotFound) { - AZ_Assert(m_cachedFileRefCounts[index] > 0, "A dedicated cache entry without references was left."); - --m_cachedFileRefCounts[index]; - if (m_cachedFileRefCounts[index] == 0) + RecursiveLockGuard lock(m_cacheMutex); + + size_t index = FindCache(filename, range); + if (index != s_fileNotFound) { - m_cachedFileNames.erase(m_cachedFileNames.begin() + index); - m_cachedFileRanges.erase(m_cachedFileRanges.begin() + index); - m_cachedFileCaches.erase(m_cachedFileCaches.begin() + index); - m_cachedFileRefCounts.erase(m_cachedFileRefCounts.begin() + index); + AZ_Assert(m_cachedFileRefCounts[index] > 0, "A dedicated cache entry without references was left."); + --m_cachedFileRefCounts[index]; + if (m_cachedFileRefCounts[index] == 0) + { + size_t hashToDelete = 0; + if (m_cachedFileRanges[index].IsEntireFile()) + { + auto combinedName = AZStd::string::format("%s/%s", m_name.c_str(), m_cachedFileNames[index].GetRelativePath()); + hashToDelete = AZStd::hash_string(combinedName.begin(), combinedName.length()); + } + else + { + auto combinedName = AZStd::string::format("%s/%s %llu:%llu", m_name.c_str(), m_cachedFileNames[index].GetRelativePath(), + m_cachedFileRanges[index].GetOffset(), m_cachedFileRanges[index].GetEndPoint()); + hashToDelete = AZStd::hash_string(combinedName.begin(), combinedName.length()); + } + m_cachedStatNamesToDelete.emplace_back(hashToDelete); + + m_cachedFileNames.erase(m_cachedFileNames.begin() + index); + m_cachedFileRanges.erase(m_cachedFileRanges.begin() + index); + m_cachedFileCaches.erase(m_cachedFileCaches.begin() + index); + m_cachedFileRefCounts.erase(m_cachedFileRefCounts.begin() + index); + } + } + else + { + AZ_Assert(false, "Attempting to destroy a dedicated cache that doesn't exist or was already destroyed."); } } - else - { - AZ_Assert(false, "Attempting to destroy a dedicated cache that doesn't exist or was already destroyed."); - } + StreamStackEntry::DestroyDedicatedCache(filename, range); } @@ -203,5 +265,40 @@ namespace AZ } return s_fileNotFound; } + + AZStd::string_view DedicatedCache::GetOrAddCachedName(size_t index, AZStd::string&& name) + { + auto iter = AZStd::find_if(m_cachedStatNames.begin(), m_cachedStatNames.end(), [index](const auto& element) + { + return element.first == index; + }); + + if (iter != m_cachedStatNames.end()) + { + return AZStd::string_view(iter->second); + } + else + { + m_cachedStatNames.push_back(AZStd::make_pair(index, AZStd::forward(name))); + return AZStd::string_view(m_cachedStatNames.back().second); + } + } + + void DedicatedCache::CleanupCachedNames() + { + for (const auto fileHash : m_cachedStatNamesToDelete) + { + auto iter = AZStd::find_if(m_cachedStatNames.begin(), m_cachedStatNames.end(), [fileHash](const auto& element) + { + return element.first == fileHash; + }); + + if (iter != m_cachedStatNames.end()) + { + m_cachedStatNames.erase(iter); + } + } + m_cachedStatNamesToDelete.clear(); + } } // namespace IO } // namespace AZ \ No newline at end of file diff --git a/dev/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h b/dev/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h index 0a83498dcb..86d4d37236 100644 --- a/dev/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h +++ b/dev/Code/Framework/AzCore/AzCore/IO/Streamer/DedicatedCache.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace AZ { @@ -52,6 +53,8 @@ namespace AZ void ReadFile(FileRequest* request); size_t FindCache(const RequestPath& filename, FileRange range); size_t FindCache(const RequestPath& filename, u64 offset); + AZStd::string_view GetOrAddCachedName(size_t index, AZStd::string&& name); + void CleanupCachedNames(); static constexpr size_t s_fileNotFound = static_cast(-1); @@ -59,6 +62,10 @@ namespace AZ AZStd::vector m_cachedFileRanges; AZStd::vector> m_cachedFileCaches; AZStd::vector m_cachedFileRefCounts; + AZStd::list> m_cachedStatNames; + AZStd::vector m_cachedStatNamesToDelete; + + mutable AZStd::recursive_mutex m_cacheMutex; u64 m_cacheSize; u32 m_blockSize;