diff options
author | Matt Mastracci <matthew@mastracci.com> | 2024-03-22 13:49:07 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-22 20:49:07 +0000 |
commit | 08ec6e5831478f6d15188e91d9a8e3c00d80c1ed (patch) | |
tree | 13a830c1ab1ee2ce5ebcffe5c0fcecd77146c8bc /runtime/js | |
parent | 9c2f9f14e749e4dd63ad02e3ca8af5fc8bd112ee (diff) |
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.
Diffstat (limited to 'runtime/js')
-rw-r--r-- | runtime/js/99_main.js | 526 |
1 files changed, 265 insertions, 261 deletions
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 1a1f78119..a47a7163e 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -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_); - } +function bootstrapMainRuntime(runtimeOptions, warmup = false) { + if (!warmup) { + if (hasBootstrapped) { + throw new Error("Worker runtime already bootstrapped"); + } - 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); + 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; + 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_); + } - if (inspectFlag) { - const consoleFromDeno = globalThis.console; - core.wrapConsole(consoleFromDeno, core.v8Console); - } + 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); - event.setEventTargetData(globalThis); - event.saveGlobalThisReference(globalThis); + 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"); - event.defineEventHandler(globalThis, "unhandledrejection"); + event.defineEventHandler(globalThis, "error"); + event.defineEventHandler(globalThis, "load"); + event.defineEventHandler(globalThis, "beforeunload"); + event.defineEventHandler(globalThis, "unload"); - 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; - }, - }, - }); + 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]); + // 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.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; - } + 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)); + // 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; + 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); + if (!warmup) { + if (hasBootstrapped) { + throw new Error("Worker runtime already bootstrapped"); + } - const consoleFromDeno = globalThis.console; - core.wrapConsole(consoleFromDeno, core.v8Console); + 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; + + // Remove bootstrapping data from the global scope + delete globalThis.__bootstrap; + delete globalThis.bootstrap; + 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); - event.setEventTargetData(globalThis); - event.saveGlobalThisReference(globalThis); + const consoleFromDeno = globalThis.console; + core.wrapConsole(consoleFromDeno, core.v8Console); - event.defineEventHandler(self, "message"); - event.defineEventHandler(self, "error", undefined, true); - event.defineEventHandler(self, "unhandledrejection"); + 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(); - }); + // `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, - ); + runtimeStart( + denoVersion, + v8Version, + tsVersion, + target, + internalName ?? name, + ); - location.setLocationHref(location_); + location.setLocationHref(location_); - globalThis.pollForMessages = pollForMessages; - globalThis.hasMessageEventListener = hasMessageEventListener; + 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]); + // 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 (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) { - // Removes the `__proto__` for security reasons. - // https://tc39.es/ecma262/#sec-get-object.prototype.__proto__ - delete Object.prototype.__proto__; - } + // Not available in workers + delete finalDenoNs.mainModule; - if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.temporal)) { - // Removes the `Temporal` API. - delete globalThis.Temporal; - delete globalThis.Date.prototype.toTemporalInstant; - } + if (!ArrayPrototypeIncludes(unstableFeatures, unstableIds.unsafeProto)) { + // Removes the `__proto__` for security reasons. + // https://tc39.es/ecma262/#sec-get-object.prototype.__proto__ + delete Object.prototype.__proto__; + } - 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)); - - const workerMetadata = maybeWorkerMetadata - ? messagePort.deserializeJsMessageData(maybeWorkerMetadata) - : undefined; - - if (nodeBootstrap) { - nodeBootstrap( - hasNodeModulesDir, - argv0, - /* runningOnMainThread */ false, - workerId, - workerMetadata, - ); + 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); |