Title: [276324] trunk/Source
Revision
276324
Author
[email protected]
Date
2021-04-20 15:42:05 -0700 (Tue, 20 Apr 2021)

Log Message

FullGCActivityCallback should use the percentage of pages uncompressed in RAM to determine deferral.
https://bugs.webkit.org/show_bug.cgi?id=224817

Reviewed by Filip Pizlo.

Source/_javascript_Core:

Right now we try to determine if too many pages are paged out by
dereferencing them and bailing out of the GC if we go over a
deadline. While this works if the only goal is to avoid causing
extensive thrashing on spinny disks (HDD), it doesn't prevent
thrashing when access to disk is fast (e.g. SSD). This is because
on fast disks the proportional time to load the memory from disk
is much lower. Additionally, on SSDs in particular we don't want
to load the pages into RAM then bail as that will force a
different page onto disk, increasing wear.

This patch switches to asking the OS if each MarkedBlock is paged
out. Then if we are over a threshold we wait until we would have
GC'd anyway. This patch uses the (maxVMGrowthFactor - 1) as the
percentage of "slow" pages (paged out or compressed) needed to
defer the GC. The idea behind that threshold is that if we add
that many pages then the same number of pages would be forced
out of RAM for us to do a GC anyway (in the limit).

* heap/BlockDirectory.cpp:
(JSC::BlockDirectory::updatePercentageOfPagedOutPages):
(JSC::BlockDirectory::isPagedOut): Deleted.
* heap/BlockDirectory.h:
* heap/FullGCActivityCallback.cpp:
(JSC::FullGCActivityCallback::doCollection):
* heap/Heap.cpp:
(JSC::Heap::isPagedOut):
* heap/Heap.h:
* heap/MarkedSpace.cpp:
(JSC::MarkedSpace::isPagedOut):
* heap/MarkedSpace.h:
* runtime/OptionsList.h:

Source/WebKit:

Add mincore to the acceptable syscall list.

* WebProcess/com.apple.WebProcess.sb.in:

Source/WTF:

Add a noexcept flavor of FunctionTraits. On Linux mincore (and probably other syscalls) are marked noexcept so the existing overloads don't work.

* wtf/FunctionTraits.h:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (276323 => 276324)


--- trunk/Source/_javascript_Core/ChangeLog	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-04-20 22:42:05 UTC (rev 276324)
@@ -1,3 +1,42 @@
+2021-04-20  Keith Miller  <[email protected]>
+
+        FullGCActivityCallback should use the percentage of pages uncompressed in RAM to determine deferral.
+        https://bugs.webkit.org/show_bug.cgi?id=224817
+
+        Reviewed by Filip Pizlo.
+
+        Right now we try to determine if too many pages are paged out by
+        dereferencing them and bailing out of the GC if we go over a
+        deadline. While this works if the only goal is to avoid causing
+        extensive thrashing on spinny disks (HDD), it doesn't prevent
+        thrashing when access to disk is fast (e.g. SSD). This is because
+        on fast disks the proportional time to load the memory from disk
+        is much lower. Additionally, on SSDs in particular we don't want
+        to load the pages into RAM then bail as that will force a
+        different page onto disk, increasing wear.
+
+        This patch switches to asking the OS if each MarkedBlock is paged
+        out. Then if we are over a threshold we wait until we would have
+        GC'd anyway. This patch uses the (maxVMGrowthFactor - 1) as the
+        percentage of "slow" pages (paged out or compressed) needed to
+        defer the GC. The idea behind that threshold is that if we add
+        that many pages then the same number of pages would be forced
+        out of RAM for us to do a GC anyway (in the limit).
+
+        * heap/BlockDirectory.cpp:
+        (JSC::BlockDirectory::updatePercentageOfPagedOutPages):
+        (JSC::BlockDirectory::isPagedOut): Deleted.
+        * heap/BlockDirectory.h:
+        * heap/FullGCActivityCallback.cpp:
+        (JSC::FullGCActivityCallback::doCollection):
+        * heap/Heap.cpp:
+        (JSC::Heap::isPagedOut):
+        * heap/Heap.h:
+        * heap/MarkedSpace.cpp:
+        (JSC::MarkedSpace::isPagedOut):
+        * heap/MarkedSpace.h:
+        * runtime/OptionsList.h:
+
 2021-04-20  Don Olmstead  <[email protected]>
 
         [CMake] Don't use FORWARDING_HEADERS_DIR for JSC GLib headers

Modified: trunk/Source/_javascript_Core/heap/BlockDirectory.cpp (276323 => 276324)


--- trunk/Source/_javascript_Core/heap/BlockDirectory.cpp	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/_javascript_Core/heap/BlockDirectory.cpp	2021-04-20 22:42:05 UTC (rev 276324)
@@ -31,6 +31,9 @@
 #include "SubspaceInlines.h"
 #include "SuperSampler.h"
 
+#include <wtf/FunctionTraits.h>
+#include <wtf/SimpleStats.h>
+
 namespace JSC {
 
 DEFINE_ALLOCATOR_WITH_HEAP_IDENTIFIER(BlockDirectory);
@@ -53,21 +56,35 @@
     m_subspace = subspace;
 }
 
-bool BlockDirectory::isPagedOut(MonotonicTime deadline)
+void BlockDirectory::updatePercentageOfPagedOutPages(SimpleStats& stats)
 {
-    unsigned itersSinceLastTimeCheck = 0;
-    for (auto* block : m_blocks) {
-        if (block)
-            block->block().populatePage();
-        ++itersSinceLastTimeCheck;
-        if (itersSinceLastTimeCheck >= Heap::s_timeCheckResolution) {
-            MonotonicTime currentTime = MonotonicTime::now();
-            if (currentTime > deadline)
-                return true;
-            itersSinceLastTimeCheck = 0;
-        }
+    // FIXME: We should figure out a solution for Windows.
+#if OS(UNIX)
+    size_t pageSize = WTF::pageSize();
+    ASSERT(!(MarkedBlock::blockSize % pageSize));
+    auto numberOfPagesInMarkedBlock = MarkedBlock::blockSize / pageSize;
+    // For some reason this can be unsigned char or char on different OSes...
+    using MincoreBufferType = std::remove_pointer_t<FunctionTraits<decltype(mincore)>::ArgumentType<2>>;
+    static_assert(std::is_same_v<std::make_unsigned_t<MincoreBufferType>, unsigned char>);
+    // pageSize is effectively a constant so this isn't really variable.
+    IGNORE_CLANG_WARNINGS_BEGIN("vla")
+    MincoreBufferType pagedBits[numberOfPagesInMarkedBlock];
+    IGNORE_CLANG_WARNINGS_END
+
+    for (auto* handle : m_blocks) {
+        if (!handle)
+            continue;
+
+        auto markedBlockSizeInBytes = static_cast<size_t>(reinterpret_cast<char*>(handle->end()) - reinterpret_cast<char*>(handle->start()));
+        RELEASE_ASSERT(markedBlockSizeInBytes / pageSize <= numberOfPagesInMarkedBlock);
+        // We could cache this in bulk (e.g. 25 MB chunks) but we haven't seen any data that it actually matters.
+        auto result = mincore(handle->start(), markedBlockSizeInBytes, pagedBits);
+        RELEASE_ASSERT(!result);
+        constexpr unsigned pageIsResidentAndNotCompressed = 1;
+        for (unsigned i = 0; i < numberOfPagesInMarkedBlock; ++i)
+            stats.add(!(pagedBits[i] & pageIsResidentAndNotCompressed));
     }
-    return false;
+#endif
 }
 
 MarkedBlock::Handle* BlockDirectory::findEmptyBlockToSteal()

Modified: trunk/Source/_javascript_Core/heap/BlockDirectory.h (276323 => 276324)


--- trunk/Source/_javascript_Core/heap/BlockDirectory.h	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/_javascript_Core/heap/BlockDirectory.h	2021-04-20 22:42:05 UTC (rev 276324)
@@ -37,6 +37,10 @@
 #include <wtf/SharedTask.h>
 #include <wtf/Vector.h>
 
+namespace WTF {
+class SimpleStats;
+}
+
 namespace JSC {
 
 class GCDeferralContext;
@@ -87,7 +91,7 @@
     // If WillDeleteBlock::Yes is passed then the block will be left in an invalid state. We do this, however, to avoid potentially paging in / decompressing old blocks to update their handle just before freeing them.
     void removeBlock(MarkedBlock::Handle*, WillDeleteBlock = WillDeleteBlock::No);
 
-    bool isPagedOut(MonotonicTime deadline);
+    void updatePercentageOfPagedOutPages(WTF::SimpleStats&);
     
     Lock& bitvectorLock() { return m_bitvectorLock; }
 

Modified: trunk/Source/_javascript_Core/heap/FullGCActivityCallback.cpp (276323 => 276324)


--- trunk/Source/_javascript_Core/heap/FullGCActivityCallback.cpp	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/_javascript_Core/heap/FullGCActivityCallback.cpp	2021-04-20 22:42:05 UTC (rev 276324)
@@ -30,10 +30,6 @@
 
 namespace JSC {
 
-#if !PLATFORM(IOS_FAMILY)
-const constexpr Seconds pagingTimeOut { 100_ms }; // Time in seconds to allow opportunistic timer to iterate over all blocks to see if the Heap is paged out.
-#endif
-
 FullGCActivityCallback::FullGCActivityCallback(Heap* heap)
     : GCActivityCallback(heap)
 {
@@ -44,11 +40,11 @@
     Heap& heap = vm.heap;
     m_didGCRecently = false;
 
-#if !PLATFORM(IOS_FAMILY)
+#if !PLATFORM(IOS_FAMILY) || PLATFORM(MACCATALYST)
     MonotonicTime startTime = MonotonicTime::now();
-    if (heap.isPagedOut(startTime + pagingTimeOut)) {
+    if (heap.isPagedOut()) {
         cancel();
-        heap.increaseLastFullGCLength(pagingTimeOut);
+        heap.increaseLastFullGCLength(MonotonicTime::now() - startTime);
         return;
     }
 #endif

Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (276323 => 276324)


--- trunk/Source/_javascript_Core/heap/Heap.cpp	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp	2021-04-20 22:42:05 UTC (rev 276324)
@@ -355,9 +355,9 @@
         WeakBlock::destroy(*this, block);
 }
 
-bool Heap::isPagedOut(MonotonicTime deadline)
+bool Heap::isPagedOut()
 {
-    return m_objectSpace.isPagedOut(deadline);
+    return m_objectSpace.isPagedOut();
 }
 
 void Heap::dumpHeapStatisticsAtVMDestruction()

Modified: trunk/Source/_javascript_Core/heap/Heap.h (276323 => 276324)


--- trunk/Source/_javascript_Core/heap/Heap.h	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/_javascript_Core/heap/Heap.h	2021-04-20 22:42:05 UTC (rev 276324)
@@ -270,7 +270,7 @@
     void deleteAllUnlinkedCodeBlocks(DeleteAllCodeEffort);
 
     void didAllocate(size_t);
-    bool isPagedOut(MonotonicTime deadline);
+    bool isPagedOut();
     
     const JITStubRoutineSet& jitStubRoutines() { return *m_jitStubRoutines; }
     

Modified: trunk/Source/_javascript_Core/heap/MarkedSpace.cpp (276323 => 276324)


--- trunk/Source/_javascript_Core/heap/MarkedSpace.cpp	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/_javascript_Core/heap/MarkedSpace.cpp	2021-04-20 22:42:05 UTC (rev 276324)
@@ -27,6 +27,7 @@
 #include "MarkedBlockInlines.h"
 #include "MarkedSpaceInlines.h"
 #include <wtf/ListDump.h>
+#include <wtf/SimpleStats.h>
 
 namespace JSC {
 
@@ -358,19 +359,19 @@
     // Nothing to do for PreciseAllocations.
 }
 
-bool MarkedSpace::isPagedOut(MonotonicTime deadline)
+bool MarkedSpace::isPagedOut()
 {
-    bool result = false;
+    SimpleStats pagedOutPagesStats;
+
     forEachDirectory(
         [&] (BlockDirectory& directory) -> IterationStatus {
-            if (directory.isPagedOut(deadline)) {
-                result = true;
-                return IterationStatus::Done;
-            }
+            directory.updatePercentageOfPagedOutPages(pagedOutPagesStats);
             return IterationStatus::Continue;
         });
     // FIXME: Consider taking PreciseAllocations into account here.
-    return result;
+    double maxHeapGrowthFactor = VM::isInMiniMode() ? Options::miniVMHeapGrowthFactor() : Options::largeHeapGrowthFactor();
+    double bailoutPercentage = Options::customFullGCCallbackBailThreshold() == -1.0 ? maxHeapGrowthFactor - 1 : Options::customFullGCCallbackBailThreshold();
+    return pagedOutPagesStats.mean() > pagedOutPagesStats.count() * bailoutPercentage;
 }
 
 void MarkedSpace::freeBlock(MarkedBlock::Handle* block)

Modified: trunk/Source/_javascript_Core/heap/MarkedSpace.h (276323 => 276324)


--- trunk/Source/_javascript_Core/heap/MarkedSpace.h	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/_javascript_Core/heap/MarkedSpace.h	2021-04-20 22:42:05 UTC (rev 276324)
@@ -148,7 +148,7 @@
     size_t size();
     size_t capacity();
 
-    bool isPagedOut(MonotonicTime deadline);
+    bool isPagedOut();
     
     HeapVersion markingVersion() const { return m_markingVersion; }
     HeapVersion newlyAllocatedVersion() const { return m_newlyAllocatedVersion; }

Modified: trunk/Source/_javascript_Core/runtime/OptionsList.h (276323 => 276324)


--- trunk/Source/_javascript_Core/runtime/OptionsList.h	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/_javascript_Core/runtime/OptionsList.h	2021-04-20 22:42:05 UTC (rev 276324)
@@ -204,6 +204,7 @@
     v(Double, largeHeapGrowthFactor, 1.24, Normal, nullptr) \
     v(Double, miniVMHeapGrowthFactor, 1.27, Normal, nullptr) \
     v(Double, criticalGCMemoryThreshold, 0.80, Normal, "percent memory in use the GC considers critical.  The collector is much more aggressive above this threshold") \
+    v(Double, customFullGCCallbackBailThreshold, -1.0, Normal, "percent of memory paged out before we bail out of timer based Full GCs. -1.0 means use (maxHeapGrowthFactor - 1)") \
     v(Double, minimumMutatorUtilization, 0, Normal, nullptr) \
     v(Double, maximumMutatorUtilization, 0.7, Normal, nullptr) \
     v(Double, epsilonMutatorUtilization, 0.01, Normal, nullptr) \

Modified: trunk/Source/WTF/ChangeLog (276323 => 276324)


--- trunk/Source/WTF/ChangeLog	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/WTF/ChangeLog	2021-04-20 22:42:05 UTC (rev 276324)
@@ -1,3 +1,14 @@
+2021-04-20  Keith Miller  <[email protected]>
+
+        FullGCActivityCallback should use the percentage of pages uncompressed in RAM to determine deferral.
+        https://bugs.webkit.org/show_bug.cgi?id=224817
+
+        Reviewed by Filip Pizlo.
+
+        Add a noexcept flavor of FunctionTraits. On Linux mincore (and probably other syscalls) are marked noexcept so the existing overloads don't work.
+
+        * wtf/FunctionTraits.h:
+
 2021-04-20  Chris Dumez  <[email protected]>
 
         Make sure we don't exit the GPUProcess too frequently while under memory pressure

Modified: trunk/Source/WTF/wtf/FunctionTraits.h (276323 => 276324)


--- trunk/Source/WTF/wtf/FunctionTraits.h	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/WTF/wtf/FunctionTraits.h	2021-04-20 22:42:05 UTC (rev 276324)
@@ -80,6 +80,14 @@
 struct FunctionTraits<Result(*)(Args...)> : public FunctionTraits<Result(Args...)> {
 };
 
+template<typename Result, typename... Args>
+struct FunctionTraits<Result(Args...) noexcept> : public FunctionTraits<Result(Args...)> {
+};
+
+template<typename Result, typename... Args>
+struct FunctionTraits<Result(*)(Args...) noexcept> : public FunctionTraits<Result(Args...)> {
+};
+
 } // namespace WTF
 
 using WTF::FunctionTraits;

Modified: trunk/Source/WebKit/ChangeLog (276323 => 276324)


--- trunk/Source/WebKit/ChangeLog	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/WebKit/ChangeLog	2021-04-20 22:42:05 UTC (rev 276324)
@@ -1,3 +1,14 @@
+2021-04-20  Keith Miller  <[email protected]>
+
+        FullGCActivityCallback should use the percentage of pages uncompressed in RAM to determine deferral.
+        https://bugs.webkit.org/show_bug.cgi?id=224817
+
+        Reviewed by Filip Pizlo.
+
+        Add mincore to the acceptable syscall list.
+
+        * WebProcess/com.apple.WebProcess.sb.in:
+
 2021-04-20  Jiewen Tan  <[email protected]>
 
         Platform Key registration does not prompt for user password when in biometric lockout

Modified: trunk/Source/WebKit/WebProcess/com.apple.WebProcess.sb.in (276323 => 276324)


--- trunk/Source/WebKit/WebProcess/com.apple.WebProcess.sb.in	2021-04-20 22:37:05 UTC (rev 276323)
+++ trunk/Source/WebKit/WebProcess/com.apple.WebProcess.sb.in	2021-04-20 22:42:05 UTC (rev 276324)
@@ -1776,6 +1776,7 @@
         (syscall-number SYS_munmap)
         (syscall-number SYS_mprotect)
         (syscall-number SYS_madvise)
+        (syscall-number SYS_mincore)
         (syscall-number SYS_fcntl)
         (syscall-number SYS_select)
         (syscall-number SYS_fsync)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to