Bug 1343483 - Determine how long functions remain syntax-parsed before they are full-parsed;r=shu

In many cases, to speed up start, compiling a ScriptSource will not
compile the functions themselves, but will rather syntax-parse them
(to check for syntax errors), leaving full compilation for
later. However, if we find ourselves in a case in which the function
is needed almost immediately, we need to full-parse the function
immediately after the syntax-parse, which is wasteful.

This changeset intends to measure how often this happens, by exporting
through Telemetry the duration between the end of the syntax-parse and
the start of the full-parse for each function.

As a memory optimization, instead of storing a timestamp for the
syntax-parse of each function, we store a single timestamp for an
entire ScriptSource. This assumes that all functions of the
ScriptSource are syntax-parsed at approximately the same instant,
which should be mostly true for everything except perhaps `eval` and
`new Function`. Then, when time comes to delazify a function, we
simply determine the time elapsed since the ScriptSource was compiled.

Histogram JS_PARSER_COMPILE_LAZY_AFTER_MS starts at 10ms (anything
smaller is often not measurable) and stops at 10s (anything larger can
safely be said to be not wasteful).

MozReview-Commit-ID: 6Ycy2OIIiAt

--HG--
extra : rebase_source : 0ccd6f51189b3ad8056e9f39e267235d68f6e2db
This commit is contained in:
David Teller 2017-03-06 21:22:00 +01:00
Родитель 88008d0832
Коммит c1b5f21196
5 изменённых файлов: 67 добавлений и 0 удалений

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

@ -397,6 +397,10 @@ BytecodeCompiler::compileScript(HandleObject environment, SharedContext* sc)
if (!maybeCompleteCompressSource())
return nullptr;
// We have just finished parsing the source. Inform the source so that we
// can compute statistics (e.g. how much time our functions remain lazy).
script->scriptSource()->recordParseEnded();
MOZ_ASSERT_IF(!cx->helperThread(), !cx->isExceptionPending());
return script;
@ -668,6 +672,24 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
.setNoScriptRval(false)
.setSelfHostingMode(false);
// Update statistics to find out if we are delazifying just after having
// lazified. Note that we are interested in the delta between end of
// syntax parsing and start of full parsing, so we do this now rather than
// after parsing below.
if (!lazy->scriptSource()->parseEnded().IsNull()) {
const mozilla::TimeDuration delta = mozilla::TimeStamp::Now() -
lazy->scriptSource()->parseEnded();
// Differentiate between web-facing and privileged code, to aid
// with optimization. Due to the number of calls to this function,
// we use `cx->runningWithTrustedPrincipals`, which is fast but
// will classify addons alongside with web-facing code.
const int HISTOGRAM = cx->runningWithTrustedPrincipals()
? JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS
: JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS;
cx->runtime()->addTelemetry(HISTOGRAM, delta.ToMilliseconds());
}
UsedNameTracker usedNames(cx);
if (!usedNames.init())
return false;

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

@ -136,6 +136,8 @@ enum {
JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS,
JS_TELEMETRY_ADDON_EXCEPTIONS,
JS_TELEMETRY_AOT_USAGE,
JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS,
JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS,
JS_TELEMETRY_END
};

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

@ -445,6 +445,14 @@ class ScriptSource
const char16_t* chunkChars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder,
size_t chunk);
// Instant at which the first parse of this source ended, or null
// if the source hasn't been parsed yet.
//
// Used for statistics purposes, to determine how much time code spends
// syntax parsed before being full parsed, to help determine whether
// our syntax parse vs. full parse heuristics are correct.
mozilla::TimeStamp parseEnded_;
public:
explicit ScriptSource()
: refs(0),
@ -603,6 +611,15 @@ class ScriptSource
// |xdrEncodeTopLevel|, and free the XDR encoder. In case of errors, the
// |buffer| is considered undefined.
bool xdrFinalizeEncoder();
const mozilla::TimeStamp parseEnded() const {
return parseEnded_;
}
// Inform `this` source that it has been fully parsed.
void recordParseEnded() {
MOZ_ASSERT(parseEnded_.IsNull());
parseEnded_ = mozilla::TimeStamp::Now();
}
};
class ScriptSourceHolder

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

@ -3154,6 +3154,12 @@ AccumulateTelemetryCallback(int id, uint32_t sample, const char* key)
case JS_TELEMETRY_AOT_USAGE:
Telemetry::Accumulate(Telemetry::JS_AOT_USAGE, sample);
break;
case JS_TELEMETRY_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS:
Telemetry::Accumulate(Telemetry::JS_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS, sample);
break;
case JS_TELEMETRY_WEB_PARSER_COMPILE_LAZY_AFTER_MS:
Telemetry::Accumulate(Telemetry::JS_WEB_PARSER_COMPILE_LAZY_AFTER_MS, sample);
break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected JS_TELEMETRY id");
}

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

@ -1084,6 +1084,26 @@
"n_values": 10,
"description": "Use of SpiderMonkey's deprecated language extensions in add-ons: ForEach=0, DestructuringForIn=1 (obsolete), LegacyGenerator=2, ExpressionClosure=3, LetBlock=4 (obsolete), LetExpression=5 (obsolete), NoSuchMethod=6 (obsolete), FlagsArgument=7 (obsolete), RegExpSourceProp=8 (obsolete), RestoredRegExpStatics=9 (obsolete), BlockScopeFunRedecl=10"
},
"JS_PRIVILEGED_PARSER_COMPILE_LAZY_AFTER_MS": {
"alert_emails": ["dteller@mozilla.com"],
"expires_in_version": "70",
"bug_numbers": [1343483],
"kind": "exponential",
"low": 10,
"high": 10000,
"n_buckets": 10,
"description": "Time elapsed between the moment a function is lazy-parsed (end of parsing of the ScriptSource) and the moment it is recompiled as non-lazy (start of compilation), in milliseconds, for privileged code."
},
"JS_WEB_PARSER_COMPILE_LAZY_AFTER_MS": {
"alert_emails": ["dteller@mozilla.com"],
"expires_in_version": "70",
"bug_numbers": [1343483],
"kind": "exponential",
"low": 10,
"high": 10000,
"n_buckets": 10,
"description": "Time elapsed between the moment a function is lazy-parsed (end of parsing of the ScriptSource) and the moment it is recompiled as non-lazy (start of compilation), in milliseconds, for web code."
},
"XUL_CACHE_DISABLED": {
"expires_in_version": "default",
"kind": "flag",