perf: lazy bootstrap options - first pass (#21164)

Move most runtime options to be lazily loaded. Constant options will be
covered in a different PR.

Towards https://github.com/denoland/deno/issues/21133
This commit is contained in:
Divy Srivastava 2023-11-12 20:52:59 -08:00 committed by GitHub
parent 39223f709b
commit 1ef617e8f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 139 additions and 149 deletions

View file

@ -406,6 +406,7 @@ fn create_cli_snapshot(snapshot_path: PathBuf) -> CreateSnapshotOutput {
deno_runtime::ops::signal::deno_signal::init_ops(),
deno_runtime::ops::tty::deno_tty::init_ops(),
deno_runtime::ops::http::deno_http_runtime::init_ops(),
deno_runtime::ops::bootstrap::deno_bootstrap::init_ops(),
cli::init_ops_and_esm(), // NOTE: This needs to be init_ops_and_esm!
];

View file

@ -137,14 +137,14 @@ const {
isNaN,
} = primordials;
let noColor = false;
let noColor = () => false;
function setNoColor(value) {
noColor = value;
function setNoColorFn(fn) {
noColor = fn;
}
function getNoColor() {
return noColor;
return noColor();
}
function assert(cond, msg = "Assertion failed.") {
@ -3646,7 +3646,7 @@ export {
inspect,
inspectArgs,
quoteString,
setNoColor,
setNoColorFn,
styles,
wrapConsole,
};

View file

@ -1,5 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
const core = globalThis.Deno.core;
const ops = core.ops;
const primordials = globalThis.__bootstrap.primordials;
const {
Promise,
@ -14,18 +16,18 @@ const LogLevel = {
Debug: 4,
};
let logLevel = 3;
let logSource = "JS";
const logSource = "JS";
function setLogLevel(level, source) {
logLevel = level;
if (source) {
logSource = source;
let logLevel_ = null;
function logLevel() {
if (logLevel_ === null) {
logLevel_ = ops.op_bootstrap_log_level() || 3;
}
return logLevel_;
}
function log(...args) {
if (logLevel >= LogLevel.Debug) {
if (logLevel() >= LogLevel.Debug) {
// if we destructure `console` off `globalThis` too early, we don't bind to
// the right console, therefore we don't log anything out.
globalThis.console.error(
@ -83,12 +85,4 @@ function getterOnly(getter) {
};
}
export {
createResolvable,
getterOnly,
log,
nonEnumerable,
readOnly,
setLogLevel,
writable,
};
export { createResolvable, getterOnly, log, nonEnumerable, readOnly, writable };

View file

@ -1,6 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
const core = globalThis.Deno.core;
const ops = core.ops;
const primordials = globalThis.__bootstrap.primordials;
const {
ObjectDefineProperties,
@ -157,19 +158,19 @@ class Navigator {
const navigator = webidl.createBranded(Navigator);
let numCpus, userAgent, language;
function setNumCpus(val) {
numCpus = val;
function memoizeLazy(f) {
let v_ = null;
return () => {
if (v_ === null) {
v_ = f();
}
return v_;
};
}
function setUserAgent(val) {
userAgent = val;
}
function setLanguage(val) {
language = val;
}
const numCpus = memoizeLazy(() => ops.op_bootstrap_numcpus());
const userAgent = memoizeLazy(() => ops.op_bootstrap_user_agent());
const language = memoizeLazy(() => ops.op_bootstrap_language());
ObjectDefineProperties(Navigator.prototype, {
hardwareConcurrency: {
@ -177,7 +178,7 @@ ObjectDefineProperties(Navigator.prototype, {
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return numCpus;
return numCpus();
},
},
userAgent: {
@ -185,7 +186,7 @@ ObjectDefineProperties(Navigator.prototype, {
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return userAgent;
return userAgent();
},
},
language: {
@ -193,7 +194,7 @@ ObjectDefineProperties(Navigator.prototype, {
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return language;
return language();
},
},
languages: {
@ -201,7 +202,7 @@ ObjectDefineProperties(Navigator.prototype, {
enumerable: true,
get() {
webidl.assertBranded(this, NavigatorPrototype);
return [language];
return [language()];
},
},
});
@ -225,7 +226,7 @@ ObjectDefineProperties(WorkerNavigator.prototype, {
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return numCpus;
return numCpus();
},
},
userAgent: {
@ -233,7 +234,7 @@ ObjectDefineProperties(WorkerNavigator.prototype, {
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return userAgent;
return userAgent();
},
},
language: {
@ -241,7 +242,7 @@ ObjectDefineProperties(WorkerNavigator.prototype, {
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return language;
return language();
},
},
languages: {
@ -249,7 +250,7 @@ ObjectDefineProperties(WorkerNavigator.prototype, {
enumerable: true,
get() {
webidl.assertBranded(this, WorkerNavigatorPrototype);
return [language];
return [language()];
},
},
});
@ -284,9 +285,7 @@ const workerRuntimeGlobalProperties = {
export {
mainRuntimeGlobalProperties,
setLanguage,
setNumCpus,
setUserAgent,
memoizeLazy,
unstableWindowOrWorkerGlobalScope,
windowOrWorkerGlobalScope,
workerRuntimeGlobalProperties,

View file

@ -26,7 +26,6 @@ const {
ObjectAssign,
ObjectDefineProperties,
ObjectDefineProperty,
ObjectFreeze,
ObjectPrototypeIsPrototypeOf,
ObjectSetPrototypeOf,
PromisePrototypeThen,
@ -50,7 +49,7 @@ import {
getNoColor,
inspectArgs,
quoteString,
setNoColor,
setNoColorFn,
wrapConsole,
} from "ext:deno_console/01_console.js";
import * as performance from "ext:deno_web/15_performance.js";
@ -67,9 +66,7 @@ import * as webidl from "ext:deno_webidl/00_webidl.js";
import DOMException from "ext:deno_web/01_dom_exception.js";
import {
mainRuntimeGlobalProperties,
setLanguage,
setNumCpus,
setUserAgent,
memoizeLazy,
unstableWindowOrWorkerGlobalScope,
windowOrWorkerGlobalScope,
workerRuntimeGlobalProperties,
@ -241,6 +238,11 @@ function opMainModule() {
return ops.op_main_module();
}
const opArgs = memoizeLazy(() => ops.op_bootstrap_args());
const opPid = memoizeLazy(() => ops.op_bootstrap_pid());
const opPpid = memoizeLazy(() => ops.op_ppid());
setNoColorFn(() => ops.op_bootstrap_no_color());
function formatException(error) {
if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, error)) {
return null;
@ -327,10 +329,6 @@ function runtimeStart(
v8Version,
tsVersion,
target,
logLevel,
noColor,
isTty,
source,
) {
core.setMacrotaskCallback(timers.handleTimerMacrotask);
core.setMacrotaskCallback(promiseRejectMacrotaskCallback);
@ -343,8 +341,6 @@ function runtimeStart(
tsVersion,
);
core.setBuildInfo(target);
util.setLogLevel(logLevel, source);
setNoColor(noColor || !isTty);
}
const pendingRejections = [];
@ -461,25 +457,16 @@ function bootstrapMainRuntime(runtimeOptions) {
const nodeBootstrap = globalThis.nodeBootstrap;
const {
0: args,
1: cpuCount,
2: logLevel,
3: denoVersion,
4: locale,
5: location_,
6: noColor,
7: isTty,
8: tsVersion,
9: unstableFlag,
10: unstableFeatures,
11: pid,
12: target,
13: v8Version,
14: userAgent,
15: inspectFlag,
// 16: enableTestingFeaturesFlag
17: hasNodeModulesDir,
18: maybeBinaryNpmCommandName,
0: denoVersion,
1: location_,
2: tsVersion,
3: unstableFlag,
4: unstableFeatures,
5: target,
6: v8Version,
7: inspectFlag,
9: hasNodeModulesDir,
10: maybeBinaryNpmCommandName,
} = runtimeOptions;
performance.setTimeOrigin(DateNow());
@ -538,27 +525,13 @@ function bootstrapMainRuntime(runtimeOptions) {
v8Version,
tsVersion,
target,
logLevel,
noColor,
isTty,
);
setNumCpus(cpuCount);
setUserAgent(userAgent);
setLanguage(locale);
let ppid = undefined;
ObjectDefineProperties(finalDenoNs, {
pid: util.readOnly(pid),
ppid: util.getterOnly(() => {
// lazy because it's expensive
if (ppid === undefined) {
ppid = ops.op_ppid();
}
return ppid;
}),
noColor: util.readOnly(noColor),
args: util.readOnly(ObjectFreeze(args)),
pid: util.getterOnly(opPid),
ppid: util.getterOnly(opPpid),
noColor: util.getterOnly(getNoColor),
args: util.getterOnly(opArgs),
mainModule: util.getterOnly(opMainModule),
});
@ -593,8 +566,6 @@ function bootstrapMainRuntime(runtimeOptions) {
// `Deno` with `Deno` namespace from "./deno.ts".
ObjectDefineProperty(globalThis, "Deno", util.readOnly(finalDenoNs));
util.log("args", args);
if (nodeBootstrap) {
nodeBootstrap(hasNodeModulesDir, maybeBinaryNpmCommandName);
}
@ -612,25 +583,16 @@ function bootstrapWorkerRuntime(
const nodeBootstrap = globalThis.nodeBootstrap;
const {
0: args,
1: cpuCount,
2: logLevel,
3: denoVersion,
4: locale,
5: location_,
6: noColor,
7: isTty,
8: tsVersion,
9: unstableFlag,
10: unstableFeatures,
11: pid,
12: target,
13: v8Version,
14: userAgent,
// 15: inspectFlag,
16: enableTestingFeaturesFlag,
17: hasNodeModulesDir,
18: maybeBinaryNpmCommandName,
0: denoVersion,
1: location_,
2: tsVersion,
3: unstableFlag,
4: unstableFeatures,
5: target,
6: v8Version,
8: enableTestingFeaturesFlag,
9: hasNodeModulesDir,
10: maybeBinaryNpmCommandName,
} = runtimeOptions;
performance.setTimeOrigin(DateNow());
@ -686,18 +648,11 @@ function bootstrapWorkerRuntime(
v8Version,
tsVersion,
target,
logLevel,
noColor,
isTty,
internalName ?? name,
);
location.setLocationHref(location_);
setNumCpus(cpuCount);
setUserAgent(userAgent);
setLanguage(locale);
globalThis.pollForMessages = pollForMessages;
// TODO(bartlomieju): deprecate --unstable
@ -710,9 +665,9 @@ function bootstrapWorkerRuntime(
}
}
ObjectDefineProperties(finalDenoNs, {
pid: util.readOnly(pid),
noColor: util.readOnly(noColor),
args: util.readOnly(ObjectFreeze(args)),
pid: util.getterOnly(opPid),
noColor: util.getterOnly(getNoColor),
args: util.getterOnly(opArgs),
});
// Setup `Deno` global - we're actually overriding already
// existing global `Deno` with `Deno` namespace from "./deno.ts".

61
runtime/ops/bootstrap.rs Normal file
View file

@ -0,0 +1,61 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::op2;
use deno_core::OpState;
use crate::BootstrapOptions;
deno_core::extension!(
deno_bootstrap,
ops = [
op_bootstrap_args,
op_bootstrap_pid,
op_bootstrap_numcpus,
op_bootstrap_user_agent,
op_bootstrap_language,
op_bootstrap_log_level,
op_bootstrap_no_color,
],
);
#[op2]
#[serde]
pub fn op_bootstrap_args(state: &mut OpState) -> Vec<String> {
state.borrow::<BootstrapOptions>().args.clone()
}
#[op2(fast)]
#[smi]
pub fn op_bootstrap_pid() -> u32 {
std::process::id()
}
#[op2(fast)]
#[smi]
pub fn op_bootstrap_numcpus(state: &mut OpState) -> u32 {
state.borrow::<BootstrapOptions>().cpu_count as u32
}
#[op2]
#[string]
pub fn op_bootstrap_user_agent(state: &mut OpState) -> String {
state.borrow::<BootstrapOptions>().user_agent.clone()
}
#[op2]
#[string]
pub fn op_bootstrap_language(state: &mut OpState) -> String {
state.borrow::<BootstrapOptions>().locale.clone()
}
#[op2(fast)]
#[smi]
pub fn op_bootstrap_log_level(state: &mut OpState) -> i32 {
state.borrow::<BootstrapOptions>().log_level as i32
}
#[op2(fast)]
pub fn op_bootstrap_no_color(state: &mut OpState) -> bool {
let options = state.borrow::<BootstrapOptions>();
options.no_color || !options.is_tty
}

View file

@ -1,5 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
pub mod bootstrap;
pub mod fs_events;
pub mod http;
pub mod os;

View file

@ -476,6 +476,7 @@ impl WebWorker {
ops::signal::deno_signal::init_ops_and_esm(),
ops::tty::deno_tty::init_ops_and_esm(),
ops::http::deno_http_runtime::init_ops_and_esm(),
ops::bootstrap::deno_bootstrap::init_ops_and_esm(),
deno_permissions_web_worker::init_ops_and_esm(
permissions,
enable_testing_features,
@ -608,6 +609,7 @@ impl WebWorker {
}
pub fn bootstrap(&mut self, options: &BootstrapOptions) {
self.js_runtime.op_state().borrow_mut().put(options.clone());
// Instead of using name for log we use `worker-${id}` because
// WebWorkers can have empty string as name.
{

View file

@ -264,7 +264,7 @@ impl MainWorker {
) -> Self {
let bootstrap_options = options.bootstrap.clone();
let mut worker = Self::from_options(main_module, permissions, options);
worker.bootstrap(&bootstrap_options);
worker.bootstrap(bootstrap_options);
worker
}
@ -380,6 +380,7 @@ impl MainWorker {
ops::signal::deno_signal::init_ops_and_esm(),
ops::tty::deno_tty::init_ops_and_esm(),
ops::http::deno_http_runtime::init_ops_and_esm(),
ops::bootstrap::deno_bootstrap::init_ops_and_esm(),
deno_permissions_worker::init_ops_and_esm(
permissions,
enable_testing_features,
@ -454,7 +455,6 @@ impl MainWorker {
let inspector = js_runtime.inspector();
op_state.borrow_mut().put(inspector);
}
let bootstrap_fn_global = {
let context = js_runtime.main_context();
let scope = &mut js_runtime.handle_scope();
@ -486,7 +486,8 @@ impl MainWorker {
}
}
pub fn bootstrap(&mut self, options: &BootstrapOptions) {
pub fn bootstrap(&mut self, options: BootstrapOptions) {
self.js_runtime.op_state().borrow_mut().put(options.clone());
let scope = &mut self.js_runtime.handle_scope();
let args = options.as_v8(scope);
let bootstrap_fn = self.bootstrap_fn_global.take().unwrap();

View file

@ -107,36 +107,20 @@ impl Default for BootstrapOptions {
/// Keep this in sync with `99_main.js`.
#[derive(Serialize)]
struct BootstrapV8<'a>(
// args
&'a Vec<String>,
// cpu_count
i32,
// log_level
i32,
// runtime_version
&'a str,
// locale
&'a str,
// location
Option<&'a str>,
// no_color
bool,
// is_tty
bool,
// ts_version
&'a str,
// unstable
bool,
// granular unstable flags
&'a [i32],
// process_id
i32,
// env!("TARGET")
&'a str,
// v8_version
&'a str,
// user_agent
&'a str,
// inspect
bool,
// enable_testing_features
@ -157,21 +141,13 @@ impl BootstrapOptions {
let ser = deno_core::serde_v8::Serializer::new(&scope);
let bootstrap = BootstrapV8(
&self.args,
self.cpu_count as _,
self.log_level as _,
&self.runtime_version,
&self.locale,
self.location.as_ref().map(|l| l.as_str()),
self.no_color,
self.is_tty,
&self.ts_version,
self.unstable,
self.unstable_features.as_ref(),
std::process::id() as _,
env!("TARGET"),
deno_core::v8_version(),
&self.user_agent,
self.inspect,
self.enable_testing_features,
self.has_node_modules_dir,