From adb2af0a2ba1285312484ca279903686c4676121 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Wed, 27 Dec 2023 01:02:51 -0500
Subject: [PATCH] heap_tracker: use linear-time mapping eviction

---
 src/common/heap_tracker.cpp | 36 +++++++++++++++++++++++++++---------
 src/common/heap_tracker.h   |  1 +
 2 files changed, 28 insertions(+), 9 deletions(-)

diff --git a/src/common/heap_tracker.cpp b/src/common/heap_tracker.cpp
index 95dc8aa1e6..6832087959 100644
--- a/src/common/heap_tracker.cpp
+++ b/src/common/heap_tracker.cpp
@@ -1,7 +1,7 @@
 // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
-#include <algorithm>
+#include <fstream>
 #include <vector>
 
 #include "common/heap_tracker.h"
@@ -11,11 +11,25 @@ namespace Common {
 
 namespace {
 
-constexpr s64 MaxResidentMapCount = 0x8000;
+s64 GetMaxPermissibleResidentMapCount() {
+    // Default value.
+    s64 value = 65530;
+
+    // Try to read how many mappings we can make.
+    std::ifstream s("/proc/sys/vm/max_map_count");
+    s >> value;
+
+    // Print, for debug.
+    LOG_INFO(HW_Memory, "Current maximum map count: {}", value);
+
+    // Allow 20000 maps for other code and to account for split inaccuracy.
+    return std::max<s64>(value - 20000, 0);
+}
 
 } // namespace
 
-HeapTracker::HeapTracker(Common::HostMemory& buffer) : m_buffer(buffer) {}
+HeapTracker::HeapTracker(Common::HostMemory& buffer)
+    : m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {}
 HeapTracker::~HeapTracker() = default;
 
 void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length,
@@ -74,8 +88,8 @@ void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_hea
             }
 
             // Erase from map.
-            it = m_mappings.erase(it);
             ASSERT(--m_map_count >= 0);
+            it = m_mappings.erase(it);
 
             // Free the item.
             delete item;
@@ -94,8 +108,8 @@ void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission p
     this->SplitHeapMap(virtual_offset, size);
 
     // Declare tracking variables.
+    const VAddr end = virtual_offset + size;
     VAddr cur = virtual_offset;
-    VAddr end = virtual_offset + size;
 
     while (cur < end) {
         VAddr next = cur;
@@ -167,7 +181,7 @@ bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) {
         it->tick = m_tick++;
 
         // Check if we need to rebuild.
-        if (m_resident_map_count > MaxResidentMapCount) {
+        if (m_resident_map_count > m_max_resident_map_count) {
             rebuild_required = true;
         }
 
@@ -193,8 +207,12 @@ void HeapTracker::RebuildSeparateHeapAddressSpace() {
 
     ASSERT(!m_resident_mappings.empty());
 
-    // Unmap so we have at least 4 maps available.
-    const size_t desired_count = std::min(m_resident_map_count, MaxResidentMapCount - 4);
+    // Dump half of the mappings.
+    //
+    // Despite being worse in theory, this has proven to be better in practice than more
+    // regularly dumping a smaller amount, because it significantly reduces average case
+    // lock contention.
+    const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2;
     const size_t evict_count = m_resident_map_count - desired_count;
     auto it = m_resident_mappings.begin();
 
@@ -247,8 +265,8 @@ void HeapTracker::SplitHeapMapLocked(VAddr offset) {
 
     // If resident, also insert into resident map.
     if (right->is_resident) {
-        m_resident_mappings.insert(*right);
         m_resident_map_count++;
+        m_resident_mappings.insert(*right);
     }
 }
 
diff --git a/src/common/heap_tracker.h b/src/common/heap_tracker.h
index cc16041d9e..ee5b0bf43a 100644
--- a/src/common/heap_tracker.h
+++ b/src/common/heap_tracker.h
@@ -86,6 +86,7 @@ private:
 
 private:
     Common::HostMemory& m_buffer;
+    const s64 m_max_resident_map_count;
 
     std::shared_mutex m_rebuild_lock{};
     std::mutex m_lock{};
-- 
GitLab