Changeset 240759 in webkit


Ignore:
Timestamp:
Jan 30, 2019 8:53:34 PM (5 years ago)
Author:
benjamin@webkit.org
Message:

<rdar://problem/47570443> Responsiveness timers are too expensive for frequent events
https://bugs.webkit.org/show_bug.cgi?id=194003

Reviewed by Geoffrey Garen.

The problem here is specific to wheel events.

For every wheel event, we start a responsiveness timer and send
a ping to the WebProcess. When the WebProcess respond, we stop the timer.

The cost of setting up the timers adds up since we get many events.

The first step to improve the situation was to switch ResponsivenessTimer
to WebCore::Timer. Since WebCore::Timer reuse the same CFRunLoopTimerRef,
we save the allocation/deallocation, insertion in the event loop, etc.

Using WebCore::Timer saves some instructions but we were still hitting
the kernel at 120hz to set up then kill each timer.
The second improvement of the patch is to avoid that by not killing the timer
when we hear back from the WebProcess.

Instead of killing the timer, we let it run and ignore the result.
When the next event comes, we reschedule the existing timer.
This brings down the timers to 60Hz, the same rate as the events.

The very last event does time out. In that case, we have a bad idle wake up:
we wake up a sleeping CPU do do nothing.
In the case of wheel events, this is fine since we saved a bunch of CPU already.
For all the other cases, I kept the normal operating mode to avoid the idle wake.

  • UIProcess/ResponsivenessTimer.cpp:

(WebKit::ResponsivenessTimer::ResponsivenessTimer):
(WebKit::ResponsivenessTimer::invalidate):
(WebKit::ResponsivenessTimer::timerFired):
(WebKit::ResponsivenessTimer::start):
(WebKit::ResponsivenessTimer::startWithLazyStop):
(WebKit::ResponsivenessTimer::stop):
(WebKit::ResponsivenessTimer::processTerminated):
(WebKit::ResponsivenessTimer::~ResponsivenessTimer): Deleted.

  • UIProcess/ResponsivenessTimer.h:
  • UIProcess/WebPageProxy.cpp:

(WebKit::WebPageProxy::sendWheelEvent):

  • UIProcess/WebProcessProxy.cpp:

(WebKit::WebProcessProxy::isResponsiveWithLazyStop):

  • UIProcess/WebProcessProxy.h:
Location:
trunk/Source/WebKit
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r240757 r240759  
     12019-01-30  Benjamin Poulain  <benjamin@webkit.org>
     2
     3        <rdar://problem/47570443> Responsiveness timers are too expensive for frequent events
     4        https://bugs.webkit.org/show_bug.cgi?id=194003
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        The problem here is specific to wheel events.
     9
     10        For every wheel event, we start a responsiveness timer and send
     11        a ping to the WebProcess. When the WebProcess respond, we stop the timer.
     12
     13        The cost of setting up the timers adds up since we get many events.
     14
     15        The first step to improve the situation was to switch ResponsivenessTimer
     16        to WebCore::Timer. Since WebCore::Timer reuse the same CFRunLoopTimerRef,
     17        we save the allocation/deallocation, insertion in the event loop, etc.
     18
     19        Using WebCore::Timer saves some instructions but we were still hitting
     20        the kernel at 120hz to set up then kill each timer.
     21        The second improvement of the patch is to avoid that by not killing the timer
     22        when we hear back from the WebProcess.
     23
     24        Instead of killing the timer, we let it run and ignore the result.
     25        When the next event comes, we reschedule the existing timer.
     26        This brings down the timers to 60Hz, the same rate as the events.
     27
     28        The very last event does time out. In that case, we have a bad idle wake up:
     29        we wake up a sleeping CPU do do nothing.
     30        In the case of wheel events, this is fine since we saved a bunch of CPU already.
     31        For all the other cases, I kept the normal operating mode to avoid the idle wake.
     32
     33        * UIProcess/ResponsivenessTimer.cpp:
     34        (WebKit::ResponsivenessTimer::ResponsivenessTimer):
     35        (WebKit::ResponsivenessTimer::invalidate):
     36        (WebKit::ResponsivenessTimer::timerFired):
     37        (WebKit::ResponsivenessTimer::start):
     38        (WebKit::ResponsivenessTimer::startWithLazyStop):
     39        (WebKit::ResponsivenessTimer::stop):
     40        (WebKit::ResponsivenessTimer::processTerminated):
     41        (WebKit::ResponsivenessTimer::~ResponsivenessTimer): Deleted.
     42        * UIProcess/ResponsivenessTimer.h:
     43        * UIProcess/WebPageProxy.cpp:
     44        (WebKit::WebPageProxy::sendWheelEvent):
     45        * UIProcess/WebProcessProxy.cpp:
     46        (WebKit::WebProcessProxy::isResponsiveWithLazyStop):
     47        * UIProcess/WebProcessProxy.h:
     48
    1492019-01-30  Daniel Bates  <dabates@apple.com>
    250
  • trunk/Source/WebKit/UIProcess/ResponsivenessTimer.cpp

    r215173 r240759  
    3333ResponsivenessTimer::ResponsivenessTimer(ResponsivenessTimer::Client& client)
    3434    : m_client(client)
    35     , m_isResponsive(true)
    36     , m_timer(RunLoop::main(), this, &ResponsivenessTimer::timerFired)
     35    , m_timer(*this, &ResponsivenessTimer::timerFired)
    3736{
    3837}
    3938
    40 ResponsivenessTimer::~ResponsivenessTimer()
    41 {
    42     m_timer.stop();
    43 }
     39ResponsivenessTimer::~ResponsivenessTimer() = default;
    4440
    4541void ResponsivenessTimer::invalidate()
    4642{
     43    m_waitingForTimer = false;
     44    m_useLazyStop = false;
    4745    m_timer.stop();
    4846}
     
    5048void ResponsivenessTimer::timerFired()
    5149{
     50    if (!m_waitingForTimer)
     51        return;
     52
     53    m_waitingForTimer = false;
     54    m_useLazyStop = false;
     55
    5256    if (!m_isResponsive)
    5357        return;
    5458
    5559    if (!m_client.mayBecomeUnresponsive()) {
     60        m_waitingForTimer = true;
    5661        m_timer.startOneShot(responsivenessTimeout);
    5762        return;
     
    6772void ResponsivenessTimer::start()
    6873{
    69     if (m_timer.isActive())
     74    if (m_waitingForTimer)
    7075        return;
    7176
     77    m_waitingForTimer = true;
     78    m_useLazyStop = false;
    7279    m_timer.startOneShot(responsivenessTimeout);
     80}
     81
     82void ResponsivenessTimer::startWithLazyStop()
     83{
     84    if (!m_waitingForTimer) {
     85        start();
     86        m_useLazyStop = true;
     87    }
    7388}
    7489
     
    8499    }
    85100
    86     m_timer.stop();
     101    m_waitingForTimer = false;
     102
     103    if (m_useLazyStop)
     104        m_useLazyStop = false;
     105    else
     106        m_timer.stop();
    87107}
    88108
     
    90110{
    91111    // Since there is no web process, we must not be waiting for it anymore.
    92     stop();
     112    m_waitingForTimer = false;
     113    m_timer.stop();
    93114}
    94115
  • trunk/Source/WebKit/UIProcess/ResponsivenessTimer.h

    r213824 r240759  
    2727#define ResponsivenessTimer_h
    2828
    29 #include <wtf/RunLoop.h>
     29#include <WebCore/Timer.h>
    3030
    3131namespace WebKit {
     
    4949   
    5050    void start();
     51
     52    // A responsiveness timer with lazy stop does not stop the underlying system timer when stopped.
     53    // Instead, it ignores the timeout if stop() was already called.
     54    //
     55    // This exists to reduce the rate at which we reset the timer.
     56    //
     57    // With a non lazy timer, we may set a timer and reset it soon after because the process is responsive.
     58    // For events, this means reseting a timer 120 times/s for a 60 Hz event source.
     59    // By not reseting the timer when responsive, we cut that in half to 60 timeout changes.
     60    void startWithLazyStop();
     61
    5162    void stop();
    5263
     
    6172
    6273    ResponsivenessTimer::Client& m_client;
    63     bool m_isResponsive;
    64 
    65     RunLoop::Timer<ResponsivenessTimer> m_timer;
     74    WebCore::Timer m_timer;
     75    bool m_isResponsive { true };
     76    bool m_waitingForTimer { false };
     77    bool m_useLazyStop { false };
    6678};
    6779
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r240725 r240759  
    23582358    // Manually ping the web process to check for responsiveness since our wheel
    23592359    // event will dispatch to a non-main thread, which always responds.
    2360     m_process->isResponsive(nullptr);
     2360    m_process->isResponsiveWithLazyStop();
    23612361}
    23622362
  • trunk/Source/WebKit/UIProcess/WebProcessProxy.cpp

    r240683 r240759  
    11181118}
    11191119
     1120void WebProcessProxy::isResponsiveWithLazyStop()
     1121{
     1122    if (m_isResponsive == NoOrMaybe::No)
     1123        return;
     1124
     1125    responsivenessTimer().startWithLazyStop();
     1126    send(Messages::WebProcess::MainThreadPing(), 0);
     1127}
     1128
    11201129bool WebProcessProxy::isJITEnabled() const
    11211130{
  • trunk/Source/WebKit/UIProcess/WebProcessProxy.h

    r240683 r240759  
    207207
    208208    void isResponsive(WTF::Function<void(bool isWebProcessResponsive)>&&);
     209    void isResponsiveWithLazyStop();
    209210    void didReceiveMainThreadPing();
    210211    void didReceiveBackgroundResponsivenessPing();
Note: See TracChangeset for help on using the changeset viewer.