Browse Source

LibJS: Improve garbage collection trigger condition

This patch triggers the collector when allocated memory doubles instead
of every 100k allocations. Which can almost half (reduce by ~48%) the
time spent on collection when loading google-maps.

This dynamic approach is inspired by some other GCs like Golang's and
Lua's and improves performance in memory heavy applications because
marking must visit old objects which will dominate the marking phase if
the GC is invoked too often.

This commit also improves the Octane Splay benchmark and almost
doubles it :^)
flofriday 1 year ago
parent
commit
a2abc5b824
2 changed files with 9 additions and 6 deletions
  1. 6 4
      Userland/Libraries/LibJS/Heap/Heap.cpp
  2. 3 2
      Userland/Libraries/LibJS/Heap/Heap.h

+ 6 - 4
Userland/Libraries/LibJS/Heap/Heap.cpp

@@ -79,14 +79,14 @@ ALWAYS_INLINE CellAllocator& Heap::allocator_for_size(size_t cell_size)
 Cell* Heap::allocate_cell(size_t size)
 {
     if (should_collect_on_every_allocation()) {
+        m_allocated_bytes_since_last_gc = 0;
         collect_garbage();
-    } else if (m_allocations_since_last_gc > m_max_allocations_between_gc) {
-        m_allocations_since_last_gc = 0;
+    } else if (m_allocated_bytes_since_last_gc + size > m_gc_bytes_threshold) {
+        m_allocated_bytes_since_last_gc = 0;
         collect_garbage();
-    } else {
-        ++m_allocations_since_last_gc;
     }
 
+    m_allocated_bytes_since_last_gc += size;
     auto& allocator = allocator_for_size(size);
     return allocator.allocate_cell(*this);
 }
@@ -354,6 +354,8 @@ void Heap::sweep_dead_cells(bool print_report, Core::ElapsedTimer const& measure
         });
     }
 
+    m_gc_bytes_threshold = live_cell_bytes > GC_MIN_BYTES_THRESHOLD ? live_cell_bytes : GC_MIN_BYTES_THRESHOLD;
+
     if (print_report) {
         Duration const time_spent = measurement_timer.elapsed_time();
         size_t live_block_count = 0;

+ 3 - 2
Userland/Libraries/LibJS/Heap/Heap.h

@@ -101,8 +101,9 @@ private:
         }
     }
 
-    size_t m_max_allocations_between_gc { 100000 };
-    size_t m_allocations_since_last_gc { 0 };
+    static constexpr size_t GC_MIN_BYTES_THRESHOLD { 4 * 1024 * 1024 };
+    size_t m_gc_bytes_threshold { GC_MIN_BYTES_THRESHOLD };
+    size_t m_allocated_bytes_since_last_gc { 0 };
 
     bool m_should_collect_on_every_allocation { false };