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
This commit is contained in:
Nicolas B. Pierron 2022-02-22 14:59:17 +00:00
Родитель 08d4fb865e
Коммит 01430ae030
4 изменённых файлов: 76 добавлений и 18 удалений

Просмотреть файл

@ -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<uint32_t>(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<uint32_t>(
StaticPrefs::dom_script_loader_full_parse_max_size());
StaticPrefs::dom_script_loader_delazification_max_size());
uint32_t script_size =
aRequest->ScriptTextLength() > 0
? static_cast<uint32_t>(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;

Просмотреть файл

@ -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);

Просмотреть файл

@ -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) \
\

Просмотреть файл

@ -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