⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 276324 in webkit


Ignore:
Timestamp:
Apr 20, 2021, 3:42:05 PM (5 years ago)
Author:
keith_miller@apple.com
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/JavaScriptCore:

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:
Location:
trunk/Source
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r276316 r276324  
     12021-04-20  Keith Miller  <keith_miller@apple.com>
     2
     3        FullGCActivityCallback should use the percentage of pages uncompressed in RAM to determine deferral.
     4        https://bugs.webkit.org/show_bug.cgi?id=224817
     5
     6        Reviewed by Filip Pizlo.
     7
     8        Right now we try to determine if too many pages are paged out by
     9        dereferencing them and bailing out of the GC if we go over a
     10        deadline. While this works if the only goal is to avoid causing
     11        extensive thrashing on spinny disks (HDD), it doesn't prevent
     12        thrashing when access to disk is fast (e.g. SSD). This is because
     13        on fast disks the proportional time to load the memory from disk
     14        is much lower. Additionally, on SSDs in particular we don't want
     15        to load the pages into RAM then bail as that will force a
     16        different page onto disk, increasing wear.
     17
     18        This patch switches to asking the OS if each MarkedBlock is paged
     19        out. Then if we are over a threshold we wait until we would have
     20        GC'd anyway. This patch uses the (maxVMGrowthFactor - 1) as the
     21        percentage of "slow" pages (paged out or compressed) needed to
     22        defer the GC. The idea behind that threshold is that if we add
     23        that many pages then the same number of pages would be forced
     24        out of RAM for us to do a GC anyway (in the limit).
     25
     26        * heap/BlockDirectory.cpp:
     27        (JSC::BlockDirectory::updatePercentageOfPagedOutPages):
     28        (JSC::BlockDirectory::isPagedOut): Deleted.
     29        * heap/BlockDirectory.h:
     30        * heap/FullGCActivityCallback.cpp:
     31        (JSC::FullGCActivityCallback::doCollection):
     32        * heap/Heap.cpp:
     33        (JSC::Heap::isPagedOut):
     34        * heap/Heap.h:
     35        * heap/MarkedSpace.cpp:
     36        (JSC::MarkedSpace::isPagedOut):
     37        * heap/MarkedSpace.h:
     38        * runtime/OptionsList.h:
     39
    1402021-04-20  Don Olmstead  <don.olmstead@sony.com>
    241
  • trunk/Source/JavaScriptCore/heap/BlockDirectory.cpp

    r276155 r276324  
    3232#include "SuperSampler.h"
    3333
     34#include <wtf/FunctionTraits.h>
     35#include <wtf/SimpleStats.h>
     36
    3437namespace JSC {
    3538
     
    5457}
    5558
    56 bool BlockDirectory::isPagedOut(MonotonicTime deadline)
    57 {
    58     unsigned itersSinceLastTimeCheck = 0;
    59     for (auto* block : m_blocks) {
    60         if (block)
    61             block->block().populatePage();
    62         ++itersSinceLastTimeCheck;
    63         if (itersSinceLastTimeCheck >= Heap::s_timeCheckResolution) {
    64             MonotonicTime currentTime = MonotonicTime::now();
    65             if (currentTime > deadline)
    66                 return true;
    67             itersSinceLastTimeCheck = 0;
    68         }
    69     }
    70     return false;
     59void BlockDirectory::updatePercentageOfPagedOutPages(SimpleStats& stats)
     60{
     61    // FIXME: We should figure out a solution for Windows.
     62#if OS(UNIX)
     63    size_t pageSize = WTF::pageSize();
     64    ASSERT(!(MarkedBlock::blockSize % pageSize));
     65    auto numberOfPagesInMarkedBlock = MarkedBlock::blockSize / pageSize;
     66    // For some reason this can be unsigned char or char on different OSes...
     67    using MincoreBufferType = std::remove_pointer_t<FunctionTraits<decltype(mincore)>::ArgumentType<2>>;
     68    static_assert(std::is_same_v<std::make_unsigned_t<MincoreBufferType>, unsigned char>);
     69    // pageSize is effectively a constant so this isn't really variable.
     70    IGNORE_CLANG_WARNINGS_BEGIN("vla")
     71    MincoreBufferType pagedBits[numberOfPagesInMarkedBlock];
     72    IGNORE_CLANG_WARNINGS_END
     73
     74    for (auto* handle : m_blocks) {
     75        if (!handle)
     76            continue;
     77
     78        auto markedBlockSizeInBytes = static_cast<size_t>(reinterpret_cast<char*>(handle->end()) - reinterpret_cast<char*>(handle->start()));
     79        RELEASE_ASSERT(markedBlockSizeInBytes / pageSize <= numberOfPagesInMarkedBlock);
     80        // We could cache this in bulk (e.g. 25 MB chunks) but we haven't seen any data that it actually matters.
     81        auto result = mincore(handle->start(), markedBlockSizeInBytes, pagedBits);
     82        RELEASE_ASSERT(!result);
     83        constexpr unsigned pageIsResidentAndNotCompressed = 1;
     84        for (unsigned i = 0; i < numberOfPagesInMarkedBlock; ++i)
     85            stats.add(!(pagedBits[i] & pageIsResidentAndNotCompressed));
     86    }
     87#endif
    7188}
    7289
  • trunk/Source/JavaScriptCore/heap/BlockDirectory.h

    r276155 r276324  
    3737#include <wtf/SharedTask.h>
    3838#include <wtf/Vector.h>
     39
     40namespace WTF {
     41class SimpleStats;
     42}
    3943
    4044namespace JSC {
     
    8892    void removeBlock(MarkedBlock::Handle*, WillDeleteBlock = WillDeleteBlock::No);
    8993
    90     bool isPagedOut(MonotonicTime deadline);
     94    void updatePercentageOfPagedOutPages(WTF::SimpleStats&);
    9195   
    9296    Lock& bitvectorLock() { return m_bitvectorLock; }
  • trunk/Source/JavaScriptCore/heap/FullGCActivityCallback.cpp

    r237266 r276324  
    3131namespace JSC {
    3232
    33 #if !PLATFORM(IOS_FAMILY)
    34 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.
    35 #endif
    36 
    3733FullGCActivityCallback::FullGCActivityCallback(Heap* heap)
    3834    : GCActivityCallback(heap)
     
    4541    m_didGCRecently = false;
    4642
    47 #if !PLATFORM(IOS_FAMILY)
     43#if !PLATFORM(IOS_FAMILY) || PLATFORM(MACCATALYST)
    4844    MonotonicTime startTime = MonotonicTime::now();
    49     if (heap.isPagedOut(startTime + pagingTimeOut)) {
     45    if (heap.isPagedOut()) {
    5046        cancel();
    51         heap.increaseLastFullGCLength(pagingTimeOut);
     47        heap.increaseLastFullGCLength(MonotonicTime::now() - startTime);
    5248        return;
    5349    }
  • trunk/Source/JavaScriptCore/heap/Heap.cpp

    r275648 r276324  
    356356}
    357357
    358 bool Heap::isPagedOut(MonotonicTime deadline)
    359 {
    360     return m_objectSpace.isPagedOut(deadline);
     358bool Heap::isPagedOut()
     359{
     360    return m_objectSpace.isPagedOut();
    361361}
    362362
  • trunk/Source/JavaScriptCore/heap/Heap.h

    r275261 r276324  
    271271
    272272    void didAllocate(size_t);
    273     bool isPagedOut(MonotonicTime deadline);
     273    bool isPagedOut();
    274274   
    275275    const JITStubRoutineSet& jitStubRoutines() { return *m_jitStubRoutines; }
  • trunk/Source/JavaScriptCore/heap/MarkedSpace.cpp

    r276155 r276324  
    2828#include "MarkedSpaceInlines.h"
    2929#include <wtf/ListDump.h>
     30#include <wtf/SimpleStats.h>
    3031
    3132namespace JSC {
     
    359360}
    360361
    361 bool MarkedSpace::isPagedOut(MonotonicTime deadline)
    362 {
    363     bool result = false;
    364     forEachDirectory(
    365         [&] (BlockDirectory& directory) -> IterationStatus {
    366             if (directory.isPagedOut(deadline)) {
    367                 result = true;
    368                 return IterationStatus::Done;
    369             }
     362bool MarkedSpace::isPagedOut()
     363{
     364    SimpleStats pagedOutPagesStats;
     365
     366    forEachDirectory(
     367        [&] (BlockDirectory& directory) -> IterationStatus {
     368            directory.updatePercentageOfPagedOutPages(pagedOutPagesStats);
    370369            return IterationStatus::Continue;
    371370        });
    372371    // FIXME: Consider taking PreciseAllocations into account here.
    373     return result;
     372    double maxHeapGrowthFactor = VM::isInMiniMode() ? Options::miniVMHeapGrowthFactor() : Options::largeHeapGrowthFactor();
     373    double bailoutPercentage = Options::customFullGCCallbackBailThreshold() == -1.0 ? maxHeapGrowthFactor - 1 : Options::customFullGCCallbackBailThreshold();
     374    return pagedOutPagesStats.mean() > pagedOutPagesStats.count() * bailoutPercentage;
    374375}
    375376
  • trunk/Source/JavaScriptCore/heap/MarkedSpace.h

    r273138 r276324  
    149149    size_t capacity();
    150150
    151     bool isPagedOut(MonotonicTime deadline);
     151    bool isPagedOut();
    152152   
    153153    HeapVersion markingVersion() const { return m_markingVersion; }
  • trunk/Source/JavaScriptCore/runtime/OptionsList.h

    r275670 r276324  
    205205    v(Double, miniVMHeapGrowthFactor, 1.27, Normal, nullptr) \
    206206    v(Double, criticalGCMemoryThreshold, 0.80, Normal, "percent memory in use the GC considers critical.  The collector is much more aggressive above this threshold") \
     207    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)") \
    207208    v(Double, minimumMutatorUtilization, 0, Normal, nullptr) \
    208209    v(Double, maximumMutatorUtilization, 0.7, Normal, nullptr) \
  • trunk/Source/WTF/ChangeLog

    r276305 r276324  
     12021-04-20  Keith Miller  <keith_miller@apple.com>
     2
     3        FullGCActivityCallback should use the percentage of pages uncompressed in RAM to determine deferral.
     4        https://bugs.webkit.org/show_bug.cgi?id=224817
     5
     6        Reviewed by Filip Pizlo.
     7
     8        Add a noexcept flavor of FunctionTraits. On Linux mincore (and probably other syscalls) are marked noexcept so the existing overloads don't work.
     9
     10        * wtf/FunctionTraits.h:
     11
    1122021-04-20  Chris Dumez  <cdumez@apple.com>
    213
  • trunk/Source/WTF/wtf/FunctionTraits.h

    r233504 r276324  
    8181};
    8282
     83template<typename Result, typename... Args>
     84struct FunctionTraits<Result(Args...) noexcept> : public FunctionTraits<Result(Args...)> {
     85};
     86
     87template<typename Result, typename... Args>
     88struct FunctionTraits<Result(*)(Args...) noexcept> : public FunctionTraits<Result(Args...)> {
     89};
     90
    8391} // namespace WTF
    8492
  • trunk/Source/WebKit/ChangeLog

    r276320 r276324  
     12021-04-20  Keith Miller  <keith_miller@apple.com>
     2
     3        FullGCActivityCallback should use the percentage of pages uncompressed in RAM to determine deferral.
     4        https://bugs.webkit.org/show_bug.cgi?id=224817
     5
     6        Reviewed by Filip Pizlo.
     7
     8        Add mincore to the acceptable syscall list.
     9
     10        * WebProcess/com.apple.WebProcess.sb.in:
     11
    1122021-04-20  Jiewen Tan  <jiewen_tan@apple.com>
    213
  • trunk/Source/WebKit/WebProcess/com.apple.WebProcess.sb.in

    r275764 r276324  
    17771777        (syscall-number SYS_mprotect)
    17781778        (syscall-number SYS_madvise)
     1779        (syscall-number SYS_mincore)
    17791780        (syscall-number SYS_fcntl)
    17801781        (syscall-number SYS_select)
Note: See TracChangeset for help on using the changeset viewer.