Changeset 265163 in webkit


Ignore:
Timestamp:
Jul 31, 2020 3:23:52 PM (4 years ago)
Author:
Chris Dumez
Message:

Launch time regression due to EndowmentStateTracker initialization in WebPageProxy constructor
https://bugs.webkit.org/show_bug.cgi?id=215029
<rdar://problem/66362333>

Reviewed by Geoffrey Garen.

EndowmentStateTracker was expensive and in a critical code path during MobileSafari app launch. Since
this code is new, this was a net app launch time regression. To address the regression, the following
changes were made:

  1. We only need the EndowmentStateTracker to monitor if the app is user-facing or not, so that we can suspend / resume media playback as needed. As a result, it is unnecessary to start monitoring user-facing state changes as soon as the WebPageProxy is constructed. Instead, we now wait until media plays on the page for the first time.
  2. It turns out that over 70% of CPU time in the EndowmentStateTracker constructor was spent getting the current endownment for the process. As a result, I updated the code to lazy populate those until they are requested for the first time.
  3. The remaining 30% of CPU time in the EndowmentStateTracker constructor was spent constructing the RBSProcessMonitor. I moved the initialization of the RBSProcessMonitor from the constructor to EndowmentStateTracker::addClient() so that we only construct a monitor when we actually have our first client.
  4. All this code was actually only useful for Catalyst app and was therefore doing work on iOS for no reason. I properly #ifdef'd out this code so that it now only applies to Catalyst apps.
  • UIProcess/EndowmentStateTracker.h:

(WebKit::EndowmentStateTracker::isVisible const):
(WebKit::EndowmentStateTracker::isUserFacing const):

  • UIProcess/EndowmentStateTracker.mm:

(WebKit::EndowmentStateTracker::stateFromEndowments):
(WebKit::EndowmentStateTracker::registerMonitorIfNecessary):
(WebKit::EndowmentStateTracker::addClient):
(WebKit::EndowmentStateTracker::ensureState const const):
(WebKit::EndowmentStateTracker::setState):
(WebKit::EndowmentStateTracker::EndowmentStateTracker): Deleted.
(WebKit::EndowmentStateTracker::setIsUserFacing): Deleted.
(WebKit::EndowmentStateTracker::setIsVisible): Deleted.

  • UIProcess/WebPageProxy.cpp:

(WebKit::m_transcodingQueue):
(WebKit::WebPageProxy::updatePlayingMediaDidChange):

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

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r265162 r265163  
     12020-07-31  Chris Dumez  <cdumez@apple.com>
     2
     3        Launch time regression due to EndowmentStateTracker initialization in WebPageProxy constructor
     4        https://bugs.webkit.org/show_bug.cgi?id=215029
     5        <rdar://problem/66362333>
     6
     7        Reviewed by Geoffrey Garen.
     8
     9        EndowmentStateTracker was expensive and in a critical code path during MobileSafari app launch. Since
     10        this code is new, this was a net app launch time regression. To address the regression, the following
     11        changes were made:
     12        1. We only need the EndowmentStateTracker to monitor if the app is user-facing or not, so that we can
     13           suspend / resume media playback as needed. As a result, it is unnecessary to start monitoring
     14           user-facing state changes as soon as the WebPageProxy is constructed. Instead, we now wait until
     15           media plays on the page for the first time.
     16        2. It turns out that over 70% of CPU time in the EndowmentStateTracker constructor was spent getting
     17           the current endownment for the process. As a result, I updated the code to lazy populate those
     18           until they are requested for the first time.
     19        3. The remaining 30% of CPU time in the EndowmentStateTracker constructor was spent constructing the
     20           RBSProcessMonitor. I moved the initialization of the RBSProcessMonitor from the constructor to
     21           EndowmentStateTracker::addClient() so that we only construct a monitor when we actually have our
     22           first client.
     23        4. All this code was actually only useful for Catalyst app and was therefore doing work on iOS for no
     24           reason. I properly #ifdef'd out this code so that it now only applies to Catalyst apps.
     25
     26        * UIProcess/EndowmentStateTracker.h:
     27        (WebKit::EndowmentStateTracker::isVisible const):
     28        (WebKit::EndowmentStateTracker::isUserFacing const):
     29        * UIProcess/EndowmentStateTracker.mm:
     30        (WebKit::EndowmentStateTracker::stateFromEndowments):
     31        (WebKit::EndowmentStateTracker::registerMonitorIfNecessary):
     32        (WebKit::EndowmentStateTracker::addClient):
     33        (WebKit::EndowmentStateTracker::ensureState const const):
     34        (WebKit::EndowmentStateTracker::setState):
     35        (WebKit::EndowmentStateTracker::EndowmentStateTracker): Deleted.
     36        (WebKit::EndowmentStateTracker::setIsUserFacing): Deleted.
     37        (WebKit::EndowmentStateTracker::setIsVisible): Deleted.
     38        * UIProcess/WebPageProxy.cpp:
     39        (WebKit::m_transcodingQueue):
     40        (WebKit::WebPageProxy::updatePlayingMediaDidChange):
     41        * UIProcess/WebPageProxy.h:
     42
    1432020-07-31  Chris Dumez  <cdumez@apple.com>
    244
  • trunk/Source/WebKit/UIProcess/EndowmentStateTracker.h

    r262858 r265163  
    3131#import <wtf/WeakHashSet.h>
    3232
     33OBJC_CLASS NSSet;
    3334OBJC_CLASS RBSProcessMonitor;
    3435
     
    4142public:
    4243    static EndowmentStateTracker& singleton();
    43     ~EndowmentStateTracker();
    4444
    4545    class Client : public CanMakeWeakPtr<Client> {
     
    5050    };
    5151
    52     bool isVisible() const { return m_isVisible; }
    53     bool isUserFacing() const { return m_isUserFacing; }
     52    bool isVisible() const { return ensureState().isVisible; }
     53    bool isUserFacing() const { return ensureState().isUserFacing; }
    5454
    5555    void addClient(Client&);
     
    6060private:
    6161    friend class NeverDestroyed<EndowmentStateTracker>;
    62     EndowmentStateTracker();
    63     void setIsUserFacing(bool);
    64     void setIsVisible(bool);
     62    EndowmentStateTracker() = default;
     63
     64    void registerMonitorIfNecessary();
     65
     66    struct State {
     67        bool isUserFacing;
     68        bool isVisible;
     69    };
     70    static State stateFromEndowments(NSSet *endowments);
     71    const State& ensureState() const;
     72    void setState(State&&);
    6573
    6674    WeakHashSet<Client> m_clients;
    6775    RetainPtr<RBSProcessMonitor> m_processMonitor;
    68     bool m_isUserFacing;
    69     bool m_isVisible;
     76    mutable Optional<State> m_state;
    7077};
    7178
  • trunk/Source/WebKit/UIProcess/EndowmentStateTracker.mm

    r262866 r265163  
    7373}
    7474
     75inline auto EndowmentStateTracker::stateFromEndowments(NSSet *endowments) -> State
     76{
     77    return State {
     78        [endowments containsObject:userfacingEndowment],
     79        [endowments containsObject:visibilityEndowment]
     80    };
     81}
     82
    7583bool EndowmentStateTracker::isApplicationForeground(pid_t pid)
    7684{
     
    8492}
    8593
    86 EndowmentStateTracker::EndowmentStateTracker()
     94void EndowmentStateTracker::registerMonitorIfNecessary()
    8795{
    88     auto processHandle = [RBSProcessHandle currentProcess];
    89     auto endowmentNamespaces = endowmentsForHandle(processHandle);
     96    if (m_processMonitor)
     97        return;
    9098
    91     m_isUserFacing = [endowmentNamespaces containsObject:userfacingEndowment];
    92     m_isVisible = [endowmentNamespaces containsObject:visibilityEndowment];
    93 
    94     m_processMonitor = [RBSProcessMonitor monitorWithConfiguration:[this, processHandle = retainPtr(processHandle)] (id<RBSProcessMonitorConfiguring> config) {
    95 
    96         RBSProcessPredicate *processPredicate = [RBSProcessPredicate predicateMatchingHandle:processHandle.get()];
     99    m_processMonitor = [RBSProcessMonitor monitorWithConfiguration:[this] (id<RBSProcessMonitorConfiguring> config) {
     100        [config setPredicates:@[[RBSProcessPredicate predicateMatchingHandle:[RBSProcessHandle currentProcess]]]];
    97101
    98102        RBSProcessStateDescriptor *stateDescriptor = [RBSProcessStateDescriptor descriptor];
    99103        stateDescriptor.endowmentNamespaces = @[visibilityEndowment, userfacingEndowment];
    100 
    101         [config setPredicates:@[processPredicate]];
    102104        [config setStateDescriptor:stateDescriptor];
    103105
    104106        [config setUpdateHandler:[this] (RBSProcessMonitor * _Nonnull monitor, RBSProcessHandle * _Nonnull process, RBSProcessStateUpdate * _Nonnull update) mutable {
    105             dispatch_async(dispatch_get_main_queue(), [this, endowmentNamespaces = retainPtr(update.state.endowmentNamespaces)] {
    106                 setIsUserFacing([endowmentNamespaces containsObject:userfacingEndowment]);
    107                 setIsVisible([endowmentNamespaces containsObject:visibilityEndowment]);
     107            dispatch_async(dispatch_get_main_queue(), [this, state = stateFromEndowments(update.state.endowmentNamespaces)]() mutable {
     108                setState(WTFMove(state));
    108109            });
    109110        }];
     
    114115{
    115116    m_clients.add(client);
     117    registerMonitorIfNecessary();
    116118}
    117119
     
    121123}
    122124
    123 void EndowmentStateTracker::setIsUserFacing(bool isUserFacing)
     125auto EndowmentStateTracker::ensureState() const -> const State&
    124126{
    125     if (m_isUserFacing == isUserFacing)
     127    if (!m_state)
     128        m_state = stateFromEndowments(endowmentsForHandle([RBSProcessHandle currentProcess]));
     129    return *m_state;
     130}
     131
     132void EndowmentStateTracker::setState(State&& state)
     133{
     134    bool isUserFacingChanged = !m_state || m_state->isUserFacing != state.isUserFacing;
     135    bool isVisibleChanged = !m_state || m_state->isVisible != state.isVisible;
     136    if (!isUserFacingChanged && !isVisibleChanged)
    126137        return;
    127     m_isUserFacing = isUserFacing;
    128138
    129     RELEASE_LOG(ViewState, "%p - EndowmentStateTracker::setIsUserFacing(%{public}s)", this, isUserFacing ? "true" : "false");
     139    m_state = WTFMove(state);
     140
     141    RELEASE_LOG(ViewState, "%p - EndowmentStateTracker::setState() isUserFacing: %{public}s isVisible: %{public}s", this, m_state->isUserFacing ? "true" : "false", m_state->isVisible ? "true" : "false");
    130142
    131143    for (auto& client : copyToVector(m_clients)) {
    132         if (client)
    133             client->isUserFacingChanged(m_isUserFacing);
    134     }
    135 }
    136 
    137 void EndowmentStateTracker::setIsVisible(bool isVisible)
    138 {
    139     if (m_isVisible == isVisible)
    140         return;
    141     m_isVisible = isVisible;
    142 
    143     RELEASE_LOG(ViewState, "%p - EndowmentStateTracker::setIsVisible(%{public}s)", this, isVisible ? "true" : "false");
    144 
    145     for (auto& client : copyToVector(m_clients)) {
    146         if (client)
    147             client->isVisibleChanged(m_isVisible);
     144        if (isUserFacingChanged && client)
     145            client->isUserFacingChanged(m_state->isUserFacing);
     146        if (isVisibleChanged && client)
     147            client->isVisibleChanged(m_state->isVisible);
    148148    }
    149149}
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r265063 r265163  
    533533    if (m_configuration->preferences()->serviceWorkerEntitlementDisabledForTesting())
    534534        disableServiceWorkerEntitlementInNetworkProcess();
    535 
    536     EndowmentStateTracker::singleton().addClient(*this);
    537535#endif
    538536
     
    577575#endif
    578576
    579 #if PLATFORM(IOS_FAMILY)
     577#if PLATFORM(MACCATALYST)
    580578    EndowmentStateTracker::singleton().removeClient(*this);
    581579#endif
     
    89398937        return;
    89408938
     8939#if PLATFORM(MACCATALYST)
     8940    // When the page starts playing media for the first time, make sure we register with
     8941    // the EndowmentStateTracker to get notifications when the application is no longer
     8942    // user-facing, so that we can appropriately suspend all media playback.
     8943    if (!m_isListeningForUserFacingStateChangeNotification) {
     8944        EndowmentStateTracker::singleton().addClient(*this);
     8945        m_isListeningForUserFacingStateChangeNotification = true;
     8946    }
     8947#endif
     8948
    89418949#if ENABLE(MEDIA_STREAM)
    89428950    WebCore::MediaProducer::MediaStateFlags oldMediaCaptureState = m_mediaState & WebCore::MediaProducer::MediaCaptureMask;
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r264812 r265163  
    122122
    123123#if PLATFORM(IOS_FAMILY)
    124 #include "EndowmentStateTracker.h"
    125124#include "GestureTypes.h"
    126125#include "WebAutocorrectionContext.h"
     126#endif
     127
     128#if PLATFORM(MACCATALYST)
     129#include "EndowmentStateTracker.h"
    127130#endif
    128131
     
    435438    , public WebCore::PlatformSpeechSynthesizerClient
    436439#endif
    437 #if PLATFORM(IOS_FAMILY)
     440#if PLATFORM(MACCATALYST)
    438441    , public EndowmentStateTracker::Client
    439442#else
     
    11891192    void processWillBecomeSuspended();
    11901193    void processWillBecomeForeground();
     1194#endif
     1195#if PLATFORM(MACCATALYST)
    11911196    void isUserFacingChanged(bool) final;
    1192     void isVisibleChanged(bool) final;
    11931197#endif
    11941198
     
    24732477    OptionSet<WebCore::ActivityState::Flag> m_activityState;
    24742478    bool m_viewWasEverInWindow { false };
     2479#if PLATFORM(MACCATALYST)
     2480    bool m_isListeningForUserFacingStateChangeNotification { false };
     2481#endif
    24752482#if PLATFORM(IOS_FAMILY)
    24762483    bool m_allowsMediaDocumentInlinePlayback { false };
  • trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm

    r264907 r265163  
    16251625}
    16261626
     1627#if PLATFORM(MACCATALYST)
    16271628void WebPageProxy::isUserFacingChanged(bool isUserFacing)
    16281629{
    1629 #if PLATFORM(MACCATALYST)
    16301630    if (!isUserFacing)
    16311631        suspendAllMediaPlayback();
    16321632    else
    16331633        resumeAllMediaPlayback();
    1634 #else
    1635     UNUSED_PARAM(isUserFacing);
     1634}
    16361635#endif
    1637 }
    1638 
    1639 void WebPageProxy::isVisibleChanged(bool isVisible)
    1640 {
    1641     UNUSED_PARAM(isVisible);
    1642 }
    16431636
    16441637#if PLATFORM(IOS)
Note: See TracChangeset for help on using the changeset viewer.