perf: warm expensive init code at snapshot time (#22714)

Slightly different approach to similar changes in #22386

Note that this doesn't use a warmup script -- we are actually just doing
more work at snapshot time.
This commit is contained in:
Matt Mastracci 2024-03-22 13:49:07 -07:00 committed by GitHub
parent 9c2f9f14e7
commit 08ec6e5831
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 401 additions and 360 deletions

View file

@ -628,6 +628,28 @@ const finalDenoNs = {
bench: () => {},
};
ObjectDefineProperties(finalDenoNs, {
pid: core.propGetterOnly(opPid),
// `ppid` should not be memoized.
// https://github.com/denoland/deno/issues/23004
ppid: core.propGetterOnly(() => op_ppid()),
noColor: core.propGetterOnly(() => op_bootstrap_no_color()),
args: core.propGetterOnly(opArgs),
mainModule: core.propGetterOnly(() => op_main_module()),
// TODO(kt3k): Remove this export at v2
// See https://github.com/denoland/deno/issues/9294
customInspect: {
get() {
warnOnDeprecatedApi(
"Deno.customInspect",
new Error().stack,
'Use `Symbol.for("Deno.customInspect")` instead.',
);
return customInspect;
},
},
});
const {
denoVersion,
tsVersion,
@ -635,155 +657,130 @@ const {
target,
} = op_snapshot_options();
function bootstrapMainRuntime(runtimeOptions) {
if (hasBootstrapped) {
throw new Error("Worker runtime already bootstrapped");
}
const nodeBootstrap = globalThis.nodeBootstrap;
const {
0: location_,
1: unstableFlag,
2: unstableFeatures,
3: inspectFlag,
5: hasNodeModulesDir,
6: argv0,
7: shouldDisableDeprecatedApiWarning,
8: shouldUseVerboseDeprecatedApiWarning,
9: future,
} = runtimeOptions;
removeImportedOps();
deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning;
verboseDeprecatedApiWarning = shouldUseVerboseDeprecatedApiWarning;
performance.setTimeOrigin(DateNow());
globalThis_ = globalThis;
// Remove bootstrapping data from the global scope
delete globalThis.__bootstrap;
delete globalThis.bootstrap;
delete globalThis.nodeBootstrap;
hasBootstrapped = true;
// If the `--location` flag isn't set, make `globalThis.location` `undefined` and
// writable, so that they can mock it themselves if they like. If the flag was
// set, define `globalThis.location`, using the provided value.
if (location_ == null) {
mainRuntimeGlobalProperties.location = {
writable: true,
};
} else {
location.setLocationHref(location_);
}
exposeUnstableFeaturesForWindowOrWorkerGlobalScope({
unstableFlag,
unstableFeatures,
});
ObjectDefineProperties(globalThis, mainRuntimeGlobalProperties);
ObjectDefineProperties(globalThis, {
// TODO(bartlomieju): in the future we might want to change the
// behavior of setting `name` to actually update the process name.
// Empty string matches what browsers do.
name: core.propWritable(""),
close: core.propWritable(windowClose),
closed: core.propGetterOnly(() => windowIsClosing),
});
ObjectSetPrototypeOf(globalThis, Window.prototype);
if (inspectFlag) {
const consoleFromDeno = globalThis.console;
core.wrapConsole(consoleFromDeno, core.v8Console);
}
event.setEventTargetData(globalThis);
event.saveGlobalThisReference(globalThis);
event.defineEventHandler(globalThis, "error");
event.defineEventHandler(globalThis, "load");
event.defineEventHandler(globalThis, "beforeunload");
event.defineEventHandler(globalThis, "unload");
event.defineEventHandler(globalThis, "unhandledrejection");
runtimeStart(
denoVersion,
v8Version,
tsVersion,
target,
);
ObjectDefineProperties(finalDenoNs, {
pid: core.propGetterOnly(opPid),
// `ppid` should not be memoized.
// https://github.com/denoland/deno/issues/23004
ppid: core.propGetterOnly(() => op_ppid()),
noColor: core.propGetterOnly(() => op_bootstrap_no_color()),
args: core.propGetterOnly(opArgs),
mainModule: core.propGetterOnly(() => op_main_module()),
// TODO(kt3k): Remove this export at v2
// See https://github.com/denoland/deno/issues/9294
customInspect: {
get() {
warnOnDeprecatedApi(
"Deno.customInspect",
new Error().stack,
'Use `Symbol.for("Deno.customInspect")` instead.',
);
return customInspect;
},
},
});
// TODO(bartlomieju): deprecate --unstable
if (unstableFlag) {
ObjectAssign(finalDenoNs, denoNsUnstable);
// TODO(bartlomieju): this is not ideal, but because we use `ObjectAssign`
// above any properties that are defined elsewhere using `Object.defineProperty`
// are lost.
let jupyterNs = undefined;
ObjectDefineProperty(finalDenoNs, "jupyter", {
get() {
if (jupyterNs) {
return jupyterNs;
}
throw new Error(
"Deno.jupyter is only available in `deno jupyter` subcommand.",
);
},
set(val) {
jupyterNs = val;
},
});
} else {
for (let i = 0; i <= unstableFeatures.length; i++) {
const id = unstableFeatures[i];
ObjectAssign(finalDenoNs, denoNsUnstableById[id]);
function bootstrapMainRuntime(runtimeOptions, warmup = false) {
if (!warmup) {
if (hasBootstrapped) {
throw new Error("Worker runtime already bootstrapped");
}
}
if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) {
// Removes the `__proto__` for security reasons.
// https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
delete Object.prototype.__proto__;
}
const {
0: location_,
1: unstableFlag,
2: unstableFeatures,
3: inspectFlag,
5: hasNodeModulesDir,
6: argv0,
7: shouldDisableDeprecatedApiWarning,
8: shouldUseVerboseDeprecatedApiWarning,
9: future,
} = runtimeOptions;
if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.temporal)) {
// Removes the `Temporal` API.
delete globalThis.Temporal;
delete globalThis.Date.prototype.toTemporalInstant;
}
removeImportedOps();
// Setup `Deno` global - we're actually overriding already existing global
// `Deno` with `Deno` namespace from "./deno.ts".
ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs));
deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning;
verboseDeprecatedApiWarning = shouldUseVerboseDeprecatedApiWarning;
performance.setTimeOrigin(DateNow());
globalThis_ = globalThis;
if (nodeBootstrap) {
nodeBootstrap(hasNodeModulesDir, argv0, /* runningOnMainThread */ true);
}
// Remove bootstrapping data from the global scope
delete globalThis.__bootstrap;
delete globalThis.bootstrap;
hasBootstrapped = true;
if (future) {
delete globalThis.window;
// If the `--location` flag isn't set, make `globalThis.location` `undefined` and
// writable, so that they can mock it themselves if they like. If the flag was
// set, define `globalThis.location`, using the provided value.
if (location_ == null) {
mainRuntimeGlobalProperties.location = {
writable: true,
};
} else {
location.setLocationHref(location_);
}
exposeUnstableFeaturesForWindowOrWorkerGlobalScope({
unstableFlag,
unstableFeatures,
});
ObjectDefineProperties(globalThis, mainRuntimeGlobalProperties);
ObjectDefineProperties(globalThis, {
// TODO(bartlomieju): in the future we might want to change the
// behavior of setting `name` to actually update the process name.
// Empty string matches what browsers do.
name: core.propWritable(""),
close: core.propWritable(windowClose),
closed: core.propGetterOnly(() => windowIsClosing),
});
ObjectSetPrototypeOf(globalThis, Window.prototype);
if (inspectFlag) {
const consoleFromDeno = globalThis.console;
core.wrapConsole(consoleFromDeno, core.v8Console);
}
event.defineEventHandler(globalThis, "error");
event.defineEventHandler(globalThis, "load");
event.defineEventHandler(globalThis, "beforeunload");
event.defineEventHandler(globalThis, "unload");
runtimeStart(
denoVersion,
v8Version,
tsVersion,
target,
);
// TODO(bartlomieju): deprecate --unstable
if (unstableFlag) {
ObjectAssign(finalDenoNs, denoNsUnstable);
// TODO(bartlomieju): this is not ideal, but because we use `ObjectAssign`
// above any properties that are defined elsewhere using `Object.defineProperty`
// are lost.
let jupyterNs = undefined;
ObjectDefineProperty(finalDenoNs, "jupyter", {
get() {
if (jupyterNs) {
return jupyterNs;
}
throw new Error(
"Deno.jupyter is only available in `deno jupyter` subcommand.",
);
},
set(val) {
jupyterNs = val;
},
});
} else {
for (let i = 0; i <= unstableFeatures.length; i++) {
const id = unstableFeatures[i];
ObjectAssign(finalDenoNs, denoNsUnstableById[id]);
}
}
if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) {
// Removes the `__proto__` for security reasons.
// https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
delete Object.prototype.__proto__;
}
if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.temporal)) {
// Removes the `Temporal` API.
delete globalThis.Temporal;
delete globalThis.Date.prototype.toTemporalInstant;
}
// Setup `Deno` global - we're actually overriding already existing global
// `Deno` with `Deno` namespace from "./deno.ts".
ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs));
if (nodeBootstrap) {
nodeBootstrap(hasNodeModulesDir, argv0, /* runningOnMainThread */ true);
}
if (future) {
delete globalThis.window;
}
} else {
// Warmup
}
}
@ -793,146 +790,153 @@ function bootstrapWorkerRuntime(
internalName,
workerId,
maybeWorkerMetadata,
warmup = false,
) {
if (hasBootstrapped) {
throw new Error("Worker runtime already bootstrapped");
}
const nodeBootstrap = globalThis.nodeBootstrap;
const {
0: location_,
1: unstableFlag,
2: unstableFeatures,
4: enableTestingFeaturesFlag,
5: hasNodeModulesDir,
6: argv0,
7: shouldDisableDeprecatedApiWarning,
8: shouldUseVerboseDeprecatedApiWarning,
9: _future,
} = runtimeOptions;
deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning;
verboseDeprecatedApiWarning = shouldUseVerboseDeprecatedApiWarning;
performance.setTimeOrigin(DateNow());
globalThis_ = globalThis;
removeImportedOps();
// Remove bootstrapping data from the global scope
delete globalThis.__bootstrap;
delete globalThis.bootstrap;
delete globalThis.nodeBootstrap;
hasBootstrapped = true;
exposeUnstableFeaturesForWindowOrWorkerGlobalScope({
unstableFlag,
unstableFeatures,
});
ObjectDefineProperties(globalThis, workerRuntimeGlobalProperties);
ObjectDefineProperties(globalThis, {
name: core.propWritable(name),
// TODO(bartlomieju): should be readonly?
close: core.propNonEnumerable(workerClose),
postMessage: core.propWritable(postMessage),
});
if (enableTestingFeaturesFlag) {
ObjectDefineProperty(
globalThis,
"importScripts",
core.propWritable(importScripts),
);
}
ObjectSetPrototypeOf(globalThis, DedicatedWorkerGlobalScope.prototype);
const consoleFromDeno = globalThis.console;
core.wrapConsole(consoleFromDeno, core.v8Console);
event.setEventTargetData(globalThis);
event.saveGlobalThisReference(globalThis);
event.defineEventHandler(self, "message");
event.defineEventHandler(self, "error", undefined, true);
event.defineEventHandler(self, "unhandledrejection");
// `Deno.exit()` is an alias to `self.close()`. Setting and exit
// code using an op in worker context is a no-op.
os.setExitHandler((_exitCode) => {
workerClose();
});
runtimeStart(
denoVersion,
v8Version,
tsVersion,
target,
internalName ?? name,
);
location.setLocationHref(location_);
globalThis.pollForMessages = pollForMessages;
globalThis.hasMessageEventListener = hasMessageEventListener;
// TODO(bartlomieju): deprecate --unstable
if (unstableFlag) {
ObjectAssign(finalDenoNs, denoNsUnstable);
} else {
for (let i = 0; i <= unstableFeatures.length; i++) {
const id = unstableFeatures[i];
ObjectAssign(finalDenoNs, denoNsUnstableById[id]);
if (!warmup) {
if (hasBootstrapped) {
throw new Error("Worker runtime already bootstrapped");
}
}
if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) {
// Removes the `__proto__` for security reasons.
// https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
delete Object.prototype.__proto__;
}
const {
0: location_,
1: unstableFlag,
2: unstableFeatures,
4: enableTestingFeaturesFlag,
5: hasNodeModulesDir,
6: argv0,
7: shouldDisableDeprecatedApiWarning,
8: shouldUseVerboseDeprecatedApiWarning,
9: _future,
} = runtimeOptions;
if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.temporal)) {
// Removes the `Temporal` API.
delete globalThis.Temporal;
delete globalThis.Date.prototype.toTemporalInstant;
}
deprecatedApiWarningDisabled = shouldDisableDeprecatedApiWarning;
verboseDeprecatedApiWarning = shouldUseVerboseDeprecatedApiWarning;
performance.setTimeOrigin(DateNow());
globalThis_ = globalThis;
ObjectDefineProperties(finalDenoNs, {
pid: core.propGetterOnly(opPid),
noColor: core.propGetterOnly(() => op_bootstrap_no_color()),
args: core.propGetterOnly(opArgs),
// TODO(kt3k): Remove this export at v2
// See https://github.com/denoland/deno/issues/9294
customInspect: {
get() {
warnOnDeprecatedApi(
"Deno.customInspect",
new Error().stack,
'Use `Symbol.for("Deno.customInspect")` instead.',
);
return customInspect;
},
},
});
// Setup `Deno` global - we're actually overriding already
// existing global `Deno` with `Deno` namespace from "./deno.ts".
ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs));
// Remove bootstrapping data from the global scope
delete globalThis.__bootstrap;
delete globalThis.bootstrap;
hasBootstrapped = true;
const workerMetadata = maybeWorkerMetadata
? messagePort.deserializeJsMessageData(maybeWorkerMetadata)
: undefined;
exposeUnstableFeaturesForWindowOrWorkerGlobalScope({
unstableFlag,
unstableFeatures,
});
ObjectDefineProperties(globalThis, workerRuntimeGlobalProperties);
ObjectDefineProperties(globalThis, {
name: core.propWritable(name),
// TODO(bartlomieju): should be readonly?
close: core.propNonEnumerable(workerClose),
postMessage: core.propWritable(postMessage),
});
if (enableTestingFeaturesFlag) {
ObjectDefineProperty(
globalThis,
"importScripts",
core.propWritable(importScripts),
);
}
ObjectSetPrototypeOf(globalThis, DedicatedWorkerGlobalScope.prototype);
if (nodeBootstrap) {
nodeBootstrap(
hasNodeModulesDir,
argv0,
/* runningOnMainThread */ false,
workerId,
workerMetadata,
const consoleFromDeno = globalThis.console;
core.wrapConsole(consoleFromDeno, core.v8Console);
event.defineEventHandler(self, "message");
event.defineEventHandler(self, "error", undefined, true);
// `Deno.exit()` is an alias to `self.close()`. Setting and exit
// code using an op in worker context is a no-op.
os.setExitHandler((_exitCode) => {
workerClose();
});
runtimeStart(
denoVersion,
v8Version,
tsVersion,
target,
internalName ?? name,
);
location.setLocationHref(location_);
globalThis.pollForMessages = pollForMessages;
globalThis.hasMessageEventListener = hasMessageEventListener;
// TODO(bartlomieju): deprecate --unstable
if (unstableFlag) {
ObjectAssign(finalDenoNs, denoNsUnstable);
} else {
for (let i = 0; i <= unstableFeatures.length; i++) {
const id = unstableFeatures[i];
ObjectAssign(finalDenoNs, denoNsUnstableById[id]);
}
}
// Not available in workers
delete finalDenoNs.mainModule;
if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) {
// Removes the `__proto__` for security reasons.
// https://tc39.es/ecma262/#sec-get-object.prototype.__proto__
delete Object.prototype.__proto__;
}
if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.temporal)) {
// Removes the `Temporal` API.
delete globalThis.Temporal;
delete globalThis.Date.prototype.toTemporalInstant;
}
// Setup `Deno` global - we're actually overriding already existing global
// `Deno` with `Deno` namespace from "./deno.ts".
ObjectDefineProperty(globalThis, "Deno", core.propReadOnly(finalDenoNs));
const workerMetadata = maybeWorkerMetadata
? messagePort.deserializeJsMessageData(maybeWorkerMetadata)
: undefined;
if (nodeBootstrap) {
nodeBootstrap(
hasNodeModulesDir,
argv0,
/* runningOnMainThread */ false,
workerId,
workerMetadata,
);
}
} else {
// Warmup
return;
}
}
const nodeBootstrap = globalThis.nodeBootstrap;
delete globalThis.nodeBootstrap;
globalThis.bootstrap = {
mainRuntime: bootstrapMainRuntime,
workerRuntime: bootstrapWorkerRuntime,
};
event.setEventTargetData(globalThis);
event.saveGlobalThisReference(globalThis);
event.defineEventHandler(globalThis, "unhandledrejection");
// Nothing listens to this, but it warms up the code paths for event dispatch
(new event.EventTarget()).dispatchEvent(new Event("warmup"));
removeImportedOps();
// Run the warmup path through node and runtime/worker bootstrap functions
bootstrapMainRuntime(undefined, true);
bootstrapWorkerRuntime(
undefined,
undefined,
undefined,
undefined,
undefined,
true,
);
nodeBootstrap(undefined, undefined, undefined, undefined, undefined, true);