Changeset 291476 in webkit


Ignore:
Timestamp:
Mar 18, 2022, 4:58:07 AM (3 years ago)
Author:
Cameron McCormack
Message:

Remove the 1ms minimum for setTimeout
https://bugs.webkit.org/show_bug.cgi?id=221124
<rdar://problem/73852354>

Reviewed by Sam Weinig.

The HTML spec makes no mention of a 1ms minimum for timers. Removing
the 1ms minimum for setTimeout results in a 0.7-2.1% improvement on
Speedometer, depending on platform and hardware.

The WPT added here demonstrates how this change can affect pages: if a
page schedules a 1ms and then a 0ms timeout in the same turn of the
event loop, then with this patch they will now be fired in the reverse
order. Firefox and Chrome do not impose a 1ms minimum, which reduces
the risk of this being a problem.

Not addressing the setTimeout 1ms minimum here, which should likely also
be removed.

While we're here, settle on "one shot" rather rather than "single
shot" as the term for timers that fire once.

Tests: imported/w3c/web-platform-tests/html/webappapis/timers/zero-settimeout.any.html

imported/w3c/web-platform-tests/html/webappapis/timers/zero-settimeout.any.worker.html

  • page/DOMTimer.h:
  • page/DOMTimer.cpp:

(WebCore::DOMTimer::DOMTimer): We must pass oneShot into
intervalClampedToMinimum, since the way isOneShot determines whether
we are a one shot timer is by checking repeatInterval, which is only
set once startRepeating is called.
(WebCore::DOMTimer::install):
(WebCore::DOMTimer::fired):
(WebCore::DOMTimer::updateTimerIntervalIfNecessary):
(WebCore::DOMTimer::isOneShot const):
(WebCore::DOMTimer::intervalClampedToMinimum const):

Location:
trunk
Files:
5 added
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r291474 r291476  
     12022-03-18  Cameron McCormack  <heycam@apple.com>
     2
     3        Remove the 1ms minimum for setTimeout
     4        https://bugs.webkit.org/show_bug.cgi?id=221124
     5        <rdar://problem/73852354>
     6
     7        Reviewed by Sam Weinig.
     8
     9        The HTML spec makes no mention of a 1ms minimum for timers. Removing
     10        the 1ms minimum for setTimeout results in a 0.7-2.1% improvement on
     11        Speedometer, depending on platform and hardware.
     12
     13        The WPT added here demonstrates how this change can affect pages: if a
     14        page schedules a 1ms and then a 0ms timeout in the same turn of the
     15        event loop, then with this patch they will now be fired in the reverse
     16        order. Firefox and Chrome do not impose a 1ms minimum, which reduces
     17        the risk of this being a problem.
     18
     19        Not addressing the setTimeout 1ms minimum here, which should likely also
     20        be removed.
     21
     22        While we're here, settle on "one shot" rather rather than "single
     23        shot" as the term for timers that fire once.
     24
     25        Tests: imported/w3c/web-platform-tests/html/webappapis/timers/zero-settimeout.any.html
     26               imported/w3c/web-platform-tests/html/webappapis/timers/zero-settimeout.any.worker.html
     27
     28        * page/DOMTimer.h:
     29        * page/DOMTimer.cpp:
     30        (WebCore::DOMTimer::DOMTimer): We must pass oneShot into
     31        intervalClampedToMinimum, since the way isOneShot determines whether
     32        we are a one shot timer is by checking repeatInterval, which is only
     33        set once startRepeating is called.
     34        (WebCore::DOMTimer::install):
     35        (WebCore::DOMTimer::fired):
     36        (WebCore::DOMTimer::updateTimerIntervalIfNecessary):
     37        (WebCore::DOMTimer::isOneShot const):
     38        (WebCore::DOMTimer::intervalClampedToMinimum const):
     39
    1402022-03-18  Antti Koivisto  <antti@apple.com>
    241
  • trunk/Source/WebCore/page/DOMTimer.cpp

    r289058 r291476  
    5050
    5151static const Seconds minIntervalForNonUserObservableChangeTimers { 1_s }; // Empirically determined to maximize battery life.
     52static const Seconds minIntervalForOneShotTimers { 0_ms };
     53static const Seconds minIntervalForRepeatingTimers { 1_ms };
    5254static const int maxTimerNestingLevel = 5;
    5355
     
    156158bool NestedTimersMap::isTrackingNestedTimers = false;
    157159
    158 DOMTimer::DOMTimer(ScriptExecutionContext& context, Function<void(ScriptExecutionContext&)>&& action, Seconds interval, bool singleShot)
     160DOMTimer::DOMTimer(ScriptExecutionContext& context, Function<void(ScriptExecutionContext&)>&& action, Seconds interval, bool oneShot)
    159161    : SuspendableTimerBase(&context)
    160162    , m_nestingLevel(context.timerNestingLevel())
     
    162164    , m_originalInterval(interval)
    163165    , m_throttleState(Undetermined)
    164     , m_currentTimerInterval(intervalClampedToMinimum())
     166    , m_currentTimerInterval(intervalClampedToMinimum(oneShot))
    165167    , m_userGestureTokenToForward(UserGestureIndicator::currentUserGesture())
    166168{
    167     if (singleShot)
     169    if (oneShot)
    168170        startOneShot(m_currentTimerInterval);
    169171    else
     
    173175DOMTimer::~DOMTimer() = default;
    174176
    175 int DOMTimer::install(ScriptExecutionContext& context, std::unique_ptr<ScheduledAction> action, Seconds timeout, bool singleShot)
     177int DOMTimer::install(ScriptExecutionContext& context, std::unique_ptr<ScheduledAction> action, Seconds timeout, bool oneShot)
    176178{
    177179    auto actionFunction = [action = WTFMove(action)](ScriptExecutionContext& context) mutable {
    178180        action->execute(context);
    179181    };
    180     return DOMTimer::install(context, WTFMove(actionFunction), timeout, singleShot);
    181 }
    182 
    183 int DOMTimer::install(ScriptExecutionContext& context, Function<void(ScriptExecutionContext&)>&& action, Seconds timeout, bool singleShot)
    184 {
    185     Ref<DOMTimer> timer = adoptRef(*new DOMTimer(context, WTFMove(action), timeout, singleShot));
     182    return DOMTimer::install(context, WTFMove(actionFunction), timeout, oneShot);
     183}
     184
     185int DOMTimer::install(ScriptExecutionContext& context, Function<void(ScriptExecutionContext&)>&& action, Seconds timeout, bool oneShot)
     186{
     187    Ref<DOMTimer> timer = adoptRef(*new DOMTimer(context, WTFMove(action), timeout, oneShot));
    186188    timer->suspendIfNeeded();
    187189
     
    191193    } while (!context.addTimeout(timer->m_timeoutId, timer.get()));
    192194
    193     InspectorInstrumentation::didInstallTimer(context, timer->m_timeoutId, timeout, singleShot);
     195    InspectorInstrumentation::didInstallTimer(context, timer->m_timeoutId, timeout, oneShot);
    194196
    195197    // Keep track of nested timer installs.
     
    199201    if (is<Document>(context)) {
    200202        auto& document = downcast<Document>(context);
    201         document.contentChangeObserver().didInstallDOMTimer(timer.get(), timeout, singleShot);
     203        document.contentChangeObserver().didInstallDOMTimer(timer.get(), timeout, oneShot);
    202204        if (DeferDOMTimersForScope::isDeferring())
    203205            document.domTimerHoldingTank().add(timer.get());
     
    289291    Ref<DOMTimer> protectedThis(*this);
    290292
    291     bool oneShot = !repeatInterval();
     293    bool oneShot = isOneShot();
    292294
    293295    ASSERT(scriptExecutionContext());
     
    351353        for (auto& idAndTimer : *nestedTimers) {
    352354            auto& timer = idAndTimer.value;
    353             if (timer->isActive() && !timer->repeatInterval())
     355            if (timer->isActive() && timer->isOneShot())
    354356                timer->updateThrottlingStateIfNecessary(fireState);
    355357        }
     
    375377        return;
    376378
    377     if (repeatInterval()) {
     379    if (isOneShot()) {
     380        LOG(DOMTimers, "%p - Updating DOMTimer's fire interval from %.2f ms to %.2f ms due to throttling.", this, previousInterval.milliseconds(), m_currentTimerInterval.milliseconds());
     381        augmentFireInterval(m_currentTimerInterval - previousInterval);
     382    } else {
    378383        ASSERT(repeatInterval() == previousInterval);
    379384        LOG(DOMTimers, "%p - Updating DOMTimer's repeat interval from %.2f ms to %.2f ms due to throttling.", this, previousInterval.milliseconds(), m_currentTimerInterval.milliseconds());
    380385        augmentRepeatInterval(m_currentTimerInterval - previousInterval);
    381     } else {
    382         LOG(DOMTimers, "%p - Updating DOMTimer's fire interval from %.2f ms to %.2f ms due to throttling.", this, previousInterval.milliseconds(), m_currentTimerInterval.milliseconds());
    383         augmentFireInterval(m_currentTimerInterval - previousInterval);
    384     }
     386    }
     387}
     388
     389bool DOMTimer::isOneShot() const
     390{
     391    return !repeatInterval();
    385392}
    386393
    387394Seconds DOMTimer::intervalClampedToMinimum() const
     395{
     396    return intervalClampedToMinimum(isOneShot());
     397}
     398
     399Seconds DOMTimer::intervalClampedToMinimum(bool oneShot) const
    388400{
    389401    ASSERT(scriptExecutionContext());
    390402    ASSERT(m_nestingLevel <= maxTimerNestingLevel);
    391403
    392     Seconds interval = std::max(1_ms, m_originalInterval);
     404    Seconds interval = std::max(oneShot ? minIntervalForOneShotTimers : minIntervalForRepeatingTimers, m_originalInterval);
    393405
    394406    // Only apply throttling to repeating timers.
  • trunk/Source/WebCore/page/DOMTimer.h

    r289058 r291476  
    7070
    7171    WEBCORE_EXPORT Seconds intervalClampedToMinimum() const;
     72    Seconds intervalClampedToMinimum(bool oneShot) const;
     73    bool isOneShot() const;
    7274
    7375    bool isDOMTimersThrottlingEnabled(Document&) const;
Note: See TracChangeset for help on using the changeset viewer.