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-16 11:01:33 +00:00
Родитель 4f7cb4fbd0
Коммит 85b56fc607
4 изменённых файлов: 75 добавлений и 18 удалений

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

@ -43,6 +43,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"
@ -1893,16 +1894,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));
}
@ -3283,20 +3284,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())
@ -3309,14 +3310,45 @@ 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 between
// OnDemandOnly and ParseEverythingEagerly.
#ifdef DEBUG
MOZ_ASSERT(uint32_t(JS::DelazificationOption::OnDemandOnly) == 0);
uint32_t count = 0;
# define _COUNT_ENTRIES(Name) count++;
# define _ASSERT_INCLUDED_ENUM(Name) \
MOZ_ASSERT(uint32_t(JS::DelazificationOption::Name) <= uint32_t(strategy));
FOREACH_DELAZIFICATION_STRATEGY(_ASSERT_INCLUDED_ENUM);
FOREACH_DELAZIFICATION_STRATEGY(_COUNT_ENTRIES);
MOZ_ASSERT(count == uint32_t(strategy) + 1);
# undef _COUNT_ENTRIES
# undef _ASSERT_INCLUDED_ENUM
#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;

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

@ -626,7 +626,9 @@ class ScriptLoader final : public 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) \
\

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

@ -3156,15 +3156,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