From 01430ae0300a94fcce60819cbb5b39e75b801b3a Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Tue, 22 Feb 2022 14:59:17 +0000 Subject: [PATCH] Bug 1753709 - JavaScript: Add a preference to switch between delazification strategies. r=arai This patch changes the way we parse JavaScript coming from Necko while loading web pages, by adding an about:config flag named javascript.options.delazification.strategy which is used to select between: 0 - On Demand 1 - Concurrent Depth First 255 - Parse Everything Eagerly Previously, we moved from On-demand delazification, to parsing everything eagerly to improve responsiveness of the browser, but we knew that more room for optimization exists. This toogle is meant to explore the space of delazification strategies, such that we can parse functions of JavaScript files on an helper thread, while the JavaScript file is being executed on the main thread. The space of delazification strategies goes from ordering the order in which functions are processed, as well as filtering functions which are processed. Not all functions have to be delazified, and if the main thread needs a function which is not parsed yet, it will fallback to parsing it on-demand. Differential Revision: https://phabricator.services.mozilla.com/D138034 --- dom/script/ScriptLoader.cpp | 53 +++++++++++++++++++----- dom/script/ScriptLoader.h | 4 +- js/public/CompileOptions.h | 3 +- modules/libpref/init/StaticPrefList.yaml | 34 ++++++++++++--- 4 files changed, 76 insertions(+), 18 deletions(-) diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index b5b9de02e73b..d6f64ef432ba 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -42,6 +42,7 @@ #include "mozilla/net/UrlClassifierFeatureFactory.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPrefs_javascript.h" #include "mozilla/StaticPrefs_network.h" #include "nsAboutProtocolUtils.h" #include "nsGkAtoms.h" @@ -1548,16 +1549,16 @@ nsresult ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest, } else { MOZ_ASSERT(aRequest->IsTextSource()); - if (ShouldFullParse(aRequest)) { - options.setForceFullParse(); + if (ShouldApplyDelazifyStrategy(aRequest)) { + ApplyDelazifyStrategy(&options); mTotalFullParseSize += aRequest->ScriptTextLength() > 0 ? static_cast(aRequest->ScriptTextLength()) : 0; LOG( - ("ScriptLoadRequest (%p): Full Parsing Enabled for url=%s " - "mTotalFullParseSize=%u", + ("ScriptLoadRequest (%p): non-on-demand-only Parsing Enabled for " + "url=%s mTotalFullParseSize=%u", aRequest, aRequest->mURI->GetSpecOrDefault().get(), mTotalFullParseSize)); } @@ -2972,20 +2973,20 @@ static bool IsInternalURIScheme(nsIURI* uri) { uri->SchemeIs("chrome"); } -bool ScriptLoader::ShouldFullParse(ScriptLoadRequest* aRequest) { +bool ScriptLoader::ShouldApplyDelazifyStrategy(ScriptLoadRequest* aRequest) { // Full parse everything if negative. - if (StaticPrefs::dom_script_loader_full_parse_max_size() < 0) { + if (StaticPrefs::dom_script_loader_delazification_max_size() < 0) { return true; } // Be conservative on machines with 2GB or less of memory. if (PhysicalSizeOfMemoryInGB() <= - StaticPrefs::dom_script_loader_full_parse_min_mem()) { + StaticPrefs::dom_script_loader_delazification_min_mem()) { return false; } uint32_t max_size = static_cast( - StaticPrefs::dom_script_loader_full_parse_max_size()); + StaticPrefs::dom_script_loader_delazification_max_size()); uint32_t script_size = aRequest->ScriptTextLength() > 0 ? static_cast(aRequest->ScriptTextLength()) @@ -2998,14 +2999,46 @@ bool ScriptLoader::ShouldFullParse(ScriptLoadRequest* aRequest) { if (LOG_ENABLED()) { nsCString url = aRequest->mURI->GetSpecOrDefault(); LOG( - ("ScriptLoadRequest (%p): Full Parsing Disabled for (%s) with size=%u" - " because mTotalFullParseSize=%u would exceed max_size=%u", + ("ScriptLoadRequest (%p): non-on-demand-only Parsing Disabled for (%s) " + "with size=%u because mTotalFullParseSize=%u would exceed max_size=%u", aRequest, url.get(), script_size, mTotalFullParseSize, max_size)); } return false; } +void ScriptLoader::ApplyDelazifyStrategy(JS::CompileOptions* aOptions) { + JS::DelazificationOption strategy = + JS::DelazificationOption::ParseEverythingEagerly; + uint32_t strategyIndex = + StaticPrefs::dom_script_loader_delazification_strategy(); + + // Assert that all enumerated values of DelazificationOption are dense between + // OnDemandOnly and ParseEverythingEagerly. +#ifdef DEBUG + uint32_t count = 0; + uint32_t mask = 0; +# define _COUNT_ENTRIES(Name) count++; +# define _MASK_ENTRIES(Name) \ + mask |= 1 << uint32_t(JS::DelazificationOption::Name); + + FOREACH_DELAZIFICATION_STRATEGY(_COUNT_ENTRIES); + MOZ_ASSERT(count == uint32_t(strategy) + 1); + FOREACH_DELAZIFICATION_STRATEGY(_MASK_ENTRIES); + MOZ_ASSERT(((mask + 1) & mask) == 0); +# undef _COUNT_ENTRIES +# undef _MASK_ENTRIES +#endif + + // Any strategy index larger than ParseEverythingEagerly would default to + // ParseEverythingEagerly. + if (strategyIndex <= uint32_t(strategy)) { + strategy = JS::DelazificationOption(uint8_t(strategyIndex)); + } + + aOptions->setEagerDelazificationStrategy(strategy); +} + bool ScriptLoader::ShouldCompileOffThread(ScriptLoadRequest* aRequest) { if (NumberOfProcessors() <= 1) { return false; diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h index b535f09114ab..28c86446f4c0 100644 --- a/dom/script/ScriptLoader.h +++ b/dom/script/ScriptLoader.h @@ -617,7 +617,9 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface { void AddAsyncRequest(ScriptLoadRequest* aRequest); bool MaybeRemovedDeferRequests(); - bool ShouldFullParse(ScriptLoadRequest* aRequest); + bool ShouldApplyDelazifyStrategy(ScriptLoadRequest* aRequest); + void ApplyDelazifyStrategy(JS::CompileOptions* aOptions); + bool ShouldCompileOffThread(ScriptLoadRequest* aRequest); void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest); diff --git a/js/public/CompileOptions.h b/js/public/CompileOptions.h index 142b11c77398..73acf48538d5 100644 --- a/js/public/CompileOptions.h +++ b/js/public/CompileOptions.h @@ -77,8 +77,7 @@ enum class AsmJSOption : uint8_t { _(OnDemandOnly) \ \ /* \ - * Delazifiy functions in a depth first traversal of the functions. (not \ - * implemented yet) \ + * Delazifiy functions in a depth first traversal of the functions. \ */ \ _(ConcurrentDepthFirst) \ \ diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index fc79eaa666fa..18c518da751b 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -3172,15 +3172,39 @@ value: 0 mirror: always -# Maximum total size for all full parsed scripts in each ScriptLoader instance. -# -1 implies full parse everything. Default value is 10MB for utf8 scripts. -- name: dom.script_loader.full_parse_max_size +# Select which parse/delazification strategy should be used while parsing +# scripts off-main-thread. (see CompileOptions.h, DelazificationOption enum) +# +# 0: On-demand only. Delazification will be triggered only on the main thread +# before the execution of the function. +# +# 1: Depth-first. Delazify all functions off-thread in the order of appearance +# in the source. +# +# 255: Parse everything eagerly, from the first parse. All functions are parsed +# at the same time as the top-level of a file. +- name: dom.script_loader.delazification.strategy + type: uint32_t + value: 255 + mirror: always + +# Maximum total size after which the delazification strategy, specified by +# `dom.script_loader.delazification.strategy`, is no longer applied, and the +# on-demand strategy is used by default. +# +# -1 disable the threshold, and delazification strategy is applied to all +# scripts. +# +# Default value is 10MB for utf8 scripts. +- name: dom.script_loader.delazification.max_size type: int32_t value: 10485760 mirror: always -# Minimum memory, in GB, required to enable full parsing. -- name: dom.script_loader.full_parse_min_mem +# Minimum memory, in GB, required to enable delazification strategy, specified +# by `dom.script_loader.delazification.strategy`. Otherwise, the on-demand +# delazification strategy is used. +- name: dom.script_loader.delazification.min_mem type: int32_t value: 2 mirror: always