Changeset 115915 in webkit


Ignore:
Timestamp:
May 2, 2012 5:14:05 PM (12 years ago)
Author:
mhahnenberg@apple.com
Message:

Opportunistic GC should give up if the Heap is paged out
https://bugs.webkit.org/show_bug.cgi?id=85411

Reviewed by Filip Pizlo.

Opportunistic GC is punishing us severely in limited memory situations because its
assumptions about how much time a collection will take are way out of whack when the Heap
has been paged out by the OS. We should add a simple detection function to the Heap that
detects if its is paged out. It will do this by iterating each block of both the MarkedSpace
and CopiedSpace. If that operation takes longer than a fixed amount of time (e.g. 100ms),
the function returns true. This function will only be run prior to an opportunistic
collection (i.e. it will not run during our normal allocation-triggered collections).

In my tests, steady state was drastically improved in high memory pressure situations (i.e.
the browser was still usable, significant reduction in SPODs). Occasionally, a normal GC
would be triggered due to pages doing things in the background, which would cause a
significant pause. As we close pages we now cause normal collections rather than full
collections, which prevents us from collecting all of the dead memory immediately. One
nice way to deal with this issue might be to do incremental sweeping.

  • heap/CopiedSpace.cpp:

(JSC::isBlockListPagedOut): Helper function to reduce code duplication when iterating over
to-space, from-space, and the oversize blocks.
(JSC):
(JSC::CopiedSpace::isPagedOut): Tries to determine whether or not CopiedSpace is paged out
by iterating all of the blocks.

  • heap/CopiedSpace.h:

(CopiedSpace):

  • heap/Heap.cpp:

(JSC::Heap::isPagedOut): Tries to determine whether the Heap is paged out by asking the
MarkedSpace and CopiedSpace if they are paged out.
(JSC):

  • heap/Heap.h:

(Heap):
(JSC::Heap::increaseLastGCLength): Added this so that the GC timer can linearly back off
each time it determines that the Heap is paged out.

  • heap/MarkedAllocator.cpp:

(JSC::MarkedAllocator::isPagedOut): Tries to determine if this particular MarkedAllocator's
list of blocks are paged out.
(JSC):

  • heap/MarkedAllocator.h:

(MarkedAllocator):

  • heap/MarkedSpace.cpp:

(JSC::MarkedSpace::isPagedOut): For each MarkedAllocator, check to see if they're paged out.

  • heap/MarkedSpace.h:

(MarkedSpace):

  • runtime/GCActivityCallback.cpp:

(JSC::DefaultGCActivityCallback::cancel):
(JSC):

  • runtime/GCActivityCallback.h:

(JSC::GCActivityCallback::cancel):
(DefaultGCActivityCallback):

  • runtime/GCActivityCallbackCF.cpp: Added a constant of 100ms for the timeout in determining

whether the Heap is paged out or not.
(JSC):
(JSC::DefaultGCActivityCallbackPlatformData::timerDidFire): Added the check to see if we
should attempt a collection based on whether or not we can iterate the blocks of the Heap in
100ms. If we can't, we cancel the timer and tell the Heap we just wasted 100ms more trying to
do a collection. This gives us a nice linear backoff so we're not constantly re-trying in
steady state paged-out-ness.
(JSC::DefaultGCActivityCallback::cancel): Added this function which, while currently doing
exactly the same thing as willCollect, is more obvious as to what it's doing when we call it
in timerDidFire.

Location:
trunk/Source/JavaScriptCore
Files:
12 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r115867 r115915  
     12012-05-02  Mark Hahnenberg  <mhahnenberg@apple.com>
     2
     3        Opportunistic GC should give up if the Heap is paged out
     4        https://bugs.webkit.org/show_bug.cgi?id=85411
     5
     6        Reviewed by Filip Pizlo.
     7
     8        Opportunistic GC is punishing us severely in limited memory situations because its
     9        assumptions about how much time a collection will take are way out of whack when the Heap
     10        has been paged out by the OS. We should add a simple detection function to the Heap that
     11        detects if its is paged out. It will do this by iterating each block of both the MarkedSpace
     12        and CopiedSpace. If that operation takes longer than a fixed amount of time (e.g. 100ms),
     13        the function returns true. This function will only be run prior to an opportunistic
     14        collection (i.e. it will not run during our normal allocation-triggered collections).
     15
     16        In my tests, steady state was drastically improved in high memory pressure situations (i.e.
     17        the browser was still usable, significant reduction in SPODs). Occasionally, a normal GC
     18        would be triggered due to pages doing things in the background, which would cause a
     19        significant pause. As we close pages we now cause normal collections rather than full
     20        collections, which prevents us from collecting all of the dead memory immediately. One
     21        nice way to deal with this issue might be to do incremental sweeping.
     22
     23
     24        * heap/CopiedSpace.cpp:
     25        (JSC::isBlockListPagedOut): Helper function to reduce code duplication when iterating over
     26        to-space, from-space, and the oversize blocks.
     27        (JSC):
     28        (JSC::CopiedSpace::isPagedOut): Tries to determine whether or not CopiedSpace is paged out
     29        by iterating all of the blocks.
     30        * heap/CopiedSpace.h:
     31        (CopiedSpace):
     32        * heap/Heap.cpp:
     33        (JSC::Heap::isPagedOut): Tries to determine whether the Heap is paged out by asking the
     34        MarkedSpace and CopiedSpace if they are paged out.
     35        (JSC):
     36        * heap/Heap.h:
     37        (Heap):
     38        (JSC::Heap::increaseLastGCLength): Added this so that the GC timer can linearly back off
     39        each time it determines that the Heap is paged out.
     40        * heap/MarkedAllocator.cpp:
     41        (JSC::MarkedAllocator::isPagedOut): Tries to determine if this particular MarkedAllocator's
     42        list of blocks are paged out.
     43        (JSC):
     44        * heap/MarkedAllocator.h:
     45        (MarkedAllocator):
     46        * heap/MarkedSpace.cpp:
     47        (JSC::MarkedSpace::isPagedOut): For each MarkedAllocator, check to see if they're paged out.
     48        * heap/MarkedSpace.h:
     49        (MarkedSpace):
     50        * runtime/GCActivityCallback.cpp:
     51        (JSC::DefaultGCActivityCallback::cancel):
     52        (JSC):
     53        * runtime/GCActivityCallback.h:
     54        (JSC::GCActivityCallback::cancel):
     55        (DefaultGCActivityCallback):
     56        * runtime/GCActivityCallbackCF.cpp: Added a constant of 100ms for the timeout in determining
     57        whether the Heap is paged out or not.
     58        (JSC):
     59        (JSC::DefaultGCActivityCallbackPlatformData::timerDidFire): Added the check to see if we
     60        should attempt a collection based on whether or not we can iterate the blocks of the Heap in
     61        100ms. If we can't, we cancel the timer and tell the Heap we just wasted 100ms more trying to
     62        do a collection. This gives us a nice linear backoff so we're not constantly re-trying in
     63        steady state paged-out-ness.
     64        (JSC::DefaultGCActivityCallback::cancel): Added this function which, while currently doing
     65        exactly the same thing as willCollect, is more obvious as to what it's doing when we call it
     66        in timerDidFire.
     67
    1682012-05-02  Yong Li  <yoli@rim.com>
    269
  • trunk/Source/JavaScriptCore/heap/CopiedSpace.cpp

    r115590 r115915  
    282282}
    283283
     284static bool isBlockListPagedOut(double deadline, DoublyLinkedList<HeapBlock>* list)
     285{
     286    unsigned itersSinceLastTimeCheck = 0;
     287    HeapBlock* current = list->head();
     288    while (current) {
     289        current = current->next();
     290        ++itersSinceLastTimeCheck;
     291        if (itersSinceLastTimeCheck >= Heap::s_timeCheckResolution) {
     292            double currentTime = WTF::monotonicallyIncreasingTime();
     293            if (currentTime > deadline)
     294                return true;
     295            itersSinceLastTimeCheck = 0;
     296        }
     297    }
     298
     299    return false;
     300}
     301
     302bool CopiedSpace::isPagedOut(double deadline)
     303{
     304    return isBlockListPagedOut(deadline, m_toSpace)
     305        || isBlockListPagedOut(deadline, m_fromSpace)
     306        || isBlockListPagedOut(deadline, &m_oversizeBlocks);
     307}
     308
    284309} // namespace JSC
  • trunk/Source/JavaScriptCore/heap/CopiedSpace.h

    r115579 r115915  
    7171
    7272    void freeAllBlocks();
     73    bool isPagedOut(double deadline);
    7374
    7475    static CopiedBlock* blockFor(void*);
  • trunk/Source/JavaScriptCore/heap/Heap.cpp

    r115861 r115915  
    346346}
    347347
     348bool Heap::isPagedOut(double deadline)
     349{
     350    return m_objectSpace.isPagedOut(deadline) || m_storageSpace.isPagedOut(deadline);
     351}
     352
    348353// The JSGlobalData is being destroyed and the collector will never run again.
    349354// Run all pending finalizers now because we won't get another chance.
  • trunk/Source/JavaScriptCore/heap/Heap.h

    r115590 r115915  
    7777        static Heap* heap(const JSCell*);
    7878
     79        // This constant determines how many blocks we iterate between checks of our
     80        // deadline when calling Heap::isPagedOut. Decreasing it will cause us to detect
     81        // overstepping our deadline more quickly, while increasing it will cause
     82        // our scan to run faster.
     83        static const unsigned s_timeCheckResolution = 16;
     84
    7985        static bool isMarked(const void*);
    8086        static bool testAndSetMarked(const void*);
     
    150156
    151157        double lastGCLength() { return m_lastGCLength; }
     158        void increaseLastGCLength(double amount) { m_lastGCLength += amount; }
    152159
    153160        JS_EXPORT_PRIVATE void discardAllCompiledCode();
    154161
    155162        void didAllocate(size_t);
     163
     164        bool isPagedOut(double deadline);
    156165
    157166    private:
  • trunk/Source/JavaScriptCore/heap/MarkedAllocator.cpp

    r115590 r115915  
    44#include "GCActivityCallback.h"
    55#include "Heap.h"
     6#include <wtf/CurrentTime.h>
    67
    78namespace JSC {
     9
     10bool MarkedAllocator::isPagedOut(double deadline)
     11{
     12    unsigned itersSinceLastTimeCheck = 0;
     13    HeapBlock* block = m_blockList.head();
     14    while (block) {
     15        block = block->next();
     16        ++itersSinceLastTimeCheck;
     17        if (itersSinceLastTimeCheck >= Heap::s_timeCheckResolution) {
     18            double currentTime = WTF::monotonicallyIncreasingTime();
     19            if (currentTime > deadline)
     20                return true;
     21            itersSinceLastTimeCheck = 0;
     22        }
     23    }
     24
     25    return false;
     26}
    827
    928inline void* MarkedAllocator::tryAllocateHelper()
  • trunk/Source/JavaScriptCore/heap/MarkedAllocator.h

    r115156 r115915  
    3333    void removeBlock(MarkedBlock*);
    3434    void init(Heap*, MarkedSpace*, size_t cellSize, bool cellsNeedDestruction);
    35    
     35
     36    bool isPagedOut(double deadline);
     37   
    3638private:
    3739    friend class LLIntOffsetsExtractor;
  • trunk/Source/JavaScriptCore/heap/MarkedSpace.cpp

    r115590 r115915  
    7171}
    7272
     73bool MarkedSpace::isPagedOut(double deadline)
     74{
     75    for (size_t cellSize = preciseStep; cellSize <= preciseCutoff; cellSize += preciseStep) {
     76        if (allocatorFor(cellSize).isPagedOut(deadline) || destructorAllocatorFor(cellSize).isPagedOut(deadline))
     77            return true;
     78    }
     79
     80    for (size_t cellSize = impreciseStep; cellSize <= impreciseCutoff; cellSize += impreciseStep) {
     81        if (allocatorFor(cellSize).isPagedOut(deadline) || destructorAllocatorFor(cellSize).isPagedOut(deadline))
     82            return true;
     83    }
     84
     85    return false;
     86}
    7387
    7488void MarkedSpace::freeBlocks(MarkedBlock* head)
  • trunk/Source/JavaScriptCore/heap/MarkedSpace.h

    r114698 r115915  
    7878    void didAddBlock(MarkedBlock*);
    7979    void didConsumeFreeList(MarkedBlock*);
     80
     81    bool isPagedOut(double deadline);
    8082
    8183private:
  • trunk/Source/JavaScriptCore/runtime/GCActivityCallback.cpp

    r115156 r115915  
    5555}
    5656
     57void DefaultGCActivityCallback::cancel()
     58{
    5759}
    5860
     61}
     62
  • trunk/Source/JavaScriptCore/runtime/GCActivityCallback.h

    r115156 r115915  
    4747    virtual void willCollect() { }
    4848    virtual void synchronize() { }
     49    virtual void cancel() { }
    4950
    5051protected:
     
    6465    virtual void willCollect();
    6566    virtual void synchronize();
     67    virtual void cancel();
    6668
    6769#if USE(CF)
  • trunk/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp

    r115156 r115915  
    5757const double maxGCTimeSlice = 0.05; // The maximum amount of CPU time we want to use for opportunistic timer-triggered collections.
    5858const double timerSlop = 2.0; // Fudge factor to avoid performance cost of resetting timer.
     59const double pagingTimeOut = 0.1; // Time in seconds to allow opportunistic timer to iterate over all blocks to see if the Heap is paged out.
    5960const CFTimeInterval decade = 60 * 60 * 24 * 365 * 10;
    6061const CFTimeInterval hour = 60 * 60;
     
    6465    Heap* heap = static_cast<Heap*>(info);
    6566    APIEntryShim shim(heap->globalData());
     67#if !PLATFORM(IOS)
     68    double startTime = WTF::monotonicallyIncreasingTime();
     69    if (heap->isPagedOut(startTime + pagingTimeOut)) {
     70        heap->activityCallback()->cancel();
     71        heap->increaseLastGCLength(pagingTimeOut);
     72        return;
     73    }
     74#endif
    6675    heap->collectAllGarbage();
    6776}
     
    136145}
    137146
     147void DefaultGCActivityCallback::cancel()
     148{
     149    cancelTimer(d.get());
    138150}
     151
     152}
Note: See TracChangeset for help on using the changeset viewer.