Browse Source

Kernel: Add a fast lookup table to the BlockBasedFS disk cache

Instead of doing a linear scan of the entire cache when doing a lookup,
we now have a nice O(1) HashMap in front of the cache.

The cache miss case can still be improved, this patch really only helps
the cache hit case.

This dramatically improves cached filesystem I/O. :^)
Andreas Kling 4 years ago
parent
commit
a6a3c20071
1 changed files with 26 additions and 11 deletions
  1. 26 11
      Kernel/FileSystem/BlockBasedFileSystem.cpp

+ 26 - 11
Kernel/FileSystem/BlockBasedFileSystem.cpp

@@ -37,6 +37,7 @@ struct CacheEntry {
     u8* data { nullptr };
     u8* data { nullptr };
     bool has_data { false };
     bool has_data { false };
     bool is_dirty { false };
     bool is_dirty { false };
+    bool is_used { false };
 };
 };
 
 
 class DiskCache {
 class DiskCache {
@@ -60,21 +61,25 @@ public:
     {
     {
         auto now = kgettimeofday().tv_sec;
         auto now = kgettimeofday().tv_sec;
 
 
-        CacheEntry* oldest_clean_entry = nullptr;
+        if (auto it = m_map.find(block_index); it != m_map.end()) {
+            auto& entry = const_cast<CacheEntry&>(entries()[it->value]);
+            ASSERT(entry.block_index == block_index);
+            entry.timestamp = now;
+            return entry;
+        }
+
+        Optional<size_t> oldest_clean_index;
         for (size_t i = 0; i < m_entry_count; ++i) {
         for (size_t i = 0; i < m_entry_count; ++i) {
             auto& entry = const_cast<CacheEntry&>(entries()[i]);
             auto& entry = const_cast<CacheEntry&>(entries()[i]);
-            if (entry.block_index == block_index) {
-                entry.timestamp = now;
-                return entry;
-            }
+            ASSERT(!entry.is_used || entry.block_index != block_index);
             if (!entry.is_dirty) {
             if (!entry.is_dirty) {
-                if (!oldest_clean_entry)
-                    oldest_clean_entry = &entry;
-                else if (entry.timestamp < oldest_clean_entry->timestamp)
-                    oldest_clean_entry = &entry;
+                if (!oldest_clean_index.has_value())
+                    oldest_clean_index = i;
+                else if (entry.timestamp < entries()[oldest_clean_index.value()].timestamp)
+                    oldest_clean_index = i;
             }
             }
         }
         }
-        if (!oldest_clean_entry) {
+        if (!oldest_clean_index.has_value()) {
             // Not a single clean entry! Flush writes and try again.
             // Not a single clean entry! Flush writes and try again.
             // NOTE: We want to make sure we only call FileBackedFS flush here,
             // NOTE: We want to make sure we only call FileBackedFS flush here,
             //       not some FileBackedFS subclass flush!
             //       not some FileBackedFS subclass flush!
@@ -83,11 +88,20 @@ public:
         }
         }
 
 
         // Replace the oldest clean entry.
         // Replace the oldest clean entry.
-        auto& new_entry = *oldest_clean_entry;
+        auto& new_entry = const_cast<CacheEntry&>(entries()[oldest_clean_index.value()]);
+
+        // If this entry was occupied, remove the previous mapping from the fast lookup table.
+        if (new_entry.block_index)
+            m_map.remove(new_entry.block_index);
+
+        // Create a fast lookup mapping from the block index to this entry.
+        m_map.set(block_index, oldest_clean_index.value());
+
         new_entry.timestamp = now;
         new_entry.timestamp = now;
         new_entry.block_index = block_index;
         new_entry.block_index = block_index;
         new_entry.has_data = false;
         new_entry.has_data = false;
         new_entry.is_dirty = false;
         new_entry.is_dirty = false;
+        new_entry.is_used = true;
         return new_entry;
         return new_entry;
     }
     }
 
 
@@ -103,6 +117,7 @@ public:
 
 
 private:
 private:
     BlockBasedFS& m_fs;
     BlockBasedFS& m_fs;
+    mutable HashMap<u32, u32> m_map;
     size_t m_entry_count { 10000 };
     size_t m_entry_count { 10000 };
     KBuffer m_cached_block_data;
     KBuffer m_cached_block_data;
     KBuffer m_entries;
     KBuffer m_entries;