summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorMatt Mastracci <matthew@mastracci.com>2023-03-21 16:33:12 -0600
committerGitHub <noreply@github.com>2023-03-21 22:33:12 +0000
commit0b4770fa7daf274ab01923fb09fd604aeb27e417 (patch)
treebee10a226239c2787caa87944a5f80783048d9f9 /cli
parent253b556e6f430012c3094d47838fe397fa588028 (diff)
perf(core) Reduce script name and script code copies (#18298)
Reduce the number of copies and allocations of script code by carrying around ownership/reference information from creation time. As an advantage, this allows us to maintain the identity of `&'static str`-based scripts and use v8's external 1-byte strings (to avoid incorrectly passing non-ASCII strings, debug `assert!`s gate all string reference paths). Benchmark results: Perf improvements -- ~0.1 - 0.2ms faster, but should reduce garbage w/external strings and reduces data copies overall. May also unlock some more interesting optimizations in the future. This requires adding some generics to functions, but manual monomorphization has been applied (outer/inner function) to avoid code bloat.
Diffstat (limited to 'cli')
-rw-r--r--cli/emit.rs7
-rw-r--r--cli/lsp/tsc.rs4
-rw-r--r--cli/module_loader.rs17
-rw-r--r--cli/standalone.rs10
-rw-r--r--cli/tools/coverage/mod.rs24
-rw-r--r--cli/tsc/mod.rs4
-rw-r--r--cli/util/text_encoding.rs36
-rw-r--r--cli/worker.rs36
8 files changed, 75 insertions, 63 deletions
diff --git a/cli/emit.rs b/cli/emit.rs
index d322fe38e..f69f70cc7 100644
--- a/cli/emit.rs
+++ b/cli/emit.rs
@@ -5,6 +5,7 @@ use crate::cache::FastInsecureHasher;
use crate::cache::ParsedSourceCache;
use deno_core::error::AnyError;
+use deno_core::ModuleCode;
use deno_core::ModuleSpecifier;
use deno_graph::MediaType;
use std::sync::Arc;
@@ -27,11 +28,11 @@ pub fn emit_parsed_source(
source: &Arc<str>,
emit_options: &deno_ast::EmitOptions,
emit_config_hash: u64,
-) -> Result<String, AnyError> {
+) -> Result<ModuleCode, AnyError> {
let source_hash = get_source_hash(source, emit_config_hash);
if let Some(emit_code) = emit_cache.get_emit_code(specifier, source_hash) {
- Ok(emit_code)
+ Ok(emit_code.into())
} else {
// this will use a cached version if it exists
let parsed_source = parsed_source_cache.get_or_parse_module(
@@ -42,6 +43,6 @@ pub fn emit_parsed_source(
let transpiled_source = parsed_source.transpile(emit_options)?;
debug_assert!(transpiled_source.source_map.is_none());
emit_cache.set_emit_code(specifier, source_hash, &transpiled_source.text);
- Ok(transpiled_source.text)
+ Ok(transpiled_source.text.into())
}
}
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index c8d8103b9..fedaae588 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -2854,7 +2854,7 @@ fn start(runtime: &mut JsRuntime, debug: bool) -> Result<(), AnyError> {
let init_config = json!({ "debug": debug });
let init_src = format!("globalThis.serverInit({init_config});");
- runtime.execute_script(&located_script_name!(), &init_src)?;
+ runtime.execute_script(located_script_name!(), init_src)?;
Ok(())
}
@@ -3442,7 +3442,7 @@ pub fn request(
};
let mark = performance.mark("request", Some(request_params.clone()));
let request_src = format!("globalThis.serverRequest({request_params});");
- runtime.execute_script(&located_script_name!(), &request_src)?;
+ runtime.execute_script(located_script_name!(), request_src)?;
let op_state = runtime.op_state();
let mut op_state = op_state.borrow_mut();
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index c5224a8b9..7f6101d80 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -14,6 +14,7 @@ use deno_core::error::AnyError;
use deno_core::futures::future::FutureExt;
use deno_core::futures::Future;
use deno_core::resolve_url;
+use deno_core::ModuleCode;
use deno_core::ModuleLoader;
use deno_core::ModuleSource;
use deno_core::ModuleSpecifier;
@@ -30,7 +31,7 @@ use std::rc::Rc;
use std::str;
struct ModuleCodeSource {
- pub code: String,
+ pub code: ModuleCode,
pub found_url: ModuleSpecifier,
pub media_type: MediaType,
}
@@ -91,7 +92,7 @@ impl CliModuleLoader {
specifier,
..
})) => Ok(ModuleCodeSource {
- code: source.to_string(),
+ code: source.into(),
found_url: specifier.clone(),
media_type: *media_type,
}),
@@ -101,13 +102,15 @@ impl CliModuleLoader {
specifier,
..
})) => {
- let code = match media_type {
+ let code: ModuleCode = match media_type {
MediaType::JavaScript
| MediaType::Unknown
| MediaType::Cjs
| MediaType::Mjs
- | MediaType::Json => source.to_string(),
- MediaType::Dts | MediaType::Dcts | MediaType::Dmts => "".to_string(),
+ | MediaType::Json => source.into(),
+ MediaType::Dts | MediaType::Dcts | MediaType::Dmts => {
+ Default::default()
+ }
MediaType::TypeScript
| MediaType::Mts
| MediaType::Cts
@@ -191,7 +194,7 @@ impl CliModuleLoader {
)?
};
ModuleCodeSource {
- code,
+ code: code.into(),
found_url: specifier.clone(),
media_type: MediaType::from_specifier(specifier),
}
@@ -208,7 +211,7 @@ impl CliModuleLoader {
code_without_source_map(code_source.code)
};
Ok(ModuleSource {
- code: code.into_bytes().into_boxed_slice(),
+ code,
module_url_specified: specifier.to_string(),
module_url_found: code_source.found_url.to_string(),
module_type: match code_source.media_type {
diff --git a/cli/standalone.rs b/cli/standalone.rs
index 8f74d50a8..254cb9de5 100644
--- a/cli/standalone.rs
+++ b/cli/standalone.rs
@@ -178,7 +178,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
async move {
if let Some((source, _)) = is_data_uri {
return Ok(deno_core::ModuleSource {
- code: source.into_bytes().into_boxed_slice(),
+ code: source.into(),
module_type: deno_core::ModuleType::JavaScript,
module_url_specified: module_specifier.to_string(),
module_url_found: module_specifier.to_string(),
@@ -192,7 +192,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
.to_owned();
Ok(deno_core::ModuleSource {
- code: code.into_bytes().into_boxed_slice(),
+ code: code.into(),
module_type: match module.kind {
eszip::ModuleKind::JavaScript => deno_core::ModuleType::JavaScript,
eszip::ModuleKind::Json => deno_core::ModuleType::Json,
@@ -384,16 +384,16 @@ pub async fn run(
options,
);
worker.execute_main_module(main_module).await?;
- worker.dispatch_load_event(&located_script_name!())?;
+ worker.dispatch_load_event(located_script_name!())?;
loop {
worker.run_event_loop(false).await?;
- if !worker.dispatch_beforeunload_event(&located_script_name!())? {
+ if !worker.dispatch_beforeunload_event(located_script_name!())? {
break;
}
}
- worker.dispatch_unload_event(&located_script_name!())?;
+ worker.dispatch_unload_event(located_script_name!())?;
std::process::exit(0);
}
diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs
index eaa087171..9fead6e37 100644
--- a/cli/tools/coverage/mod.rs
+++ b/cli/tools/coverage/mod.rs
@@ -20,6 +20,7 @@ use deno_core::serde_json;
use deno_core::sourcemap::SourceMap;
use deno_core::url::Url;
use deno_core::LocalInspectorSession;
+use deno_core::ModuleCode;
use regex::Regex;
use std::fs;
use std::fs::File;
@@ -170,16 +171,16 @@ struct CoverageReport {
fn generate_coverage_report(
script_coverage: &ScriptCoverage,
- script_source: &str,
+ script_source: String,
maybe_source_map: &Option<Vec<u8>>,
output: &Option<PathBuf>,
) -> CoverageReport {
let maybe_source_map = maybe_source_map
.as_ref()
.map(|source_map| SourceMap::from_slice(source_map).unwrap());
- let text_lines = TextLines::new(script_source);
+ let text_lines = TextLines::new(&script_source);
- let comment_ranges = deno_ast::lex(script_source, MediaType::JavaScript)
+ let comment_ranges = deno_ast::lex(&script_source, MediaType::JavaScript)
.into_iter()
.filter(|item| {
matches!(item.inner, deno_ast::TokenOrComment::Comment { .. })
@@ -680,14 +681,14 @@ pub async fn cover_files(
})?;
// Check if file was transpiled
- let original_source = &file.source;
- let transpiled_code = match file.media_type {
+ let original_source = file.source.clone();
+ let transpiled_code: ModuleCode = match file.media_type {
MediaType::JavaScript
| MediaType::Unknown
| MediaType::Cjs
| MediaType::Mjs
- | MediaType::Json => file.source.as_ref().to_string(),
- MediaType::Dts | MediaType::Dmts | MediaType::Dcts => "".to_string(),
+ | MediaType::Json => file.source.into(),
+ MediaType::Dts | MediaType::Dmts | MediaType::Dcts => Default::default(),
MediaType::TypeScript
| MediaType::Jsx
| MediaType::Mts
@@ -695,7 +696,7 @@ pub async fn cover_files(
| MediaType::Tsx => {
let source_hash = get_source_hash(&file.source, ps.emit_options_hash);
match ps.emit_cache.get_emit_code(&file.specifier, source_hash) {
- Some(code) => code,
+ Some(code) => code.into(),
None => {
return Err(anyhow!(
"Missing transpiled source code for: \"{}\".
@@ -710,15 +711,16 @@ pub async fn cover_files(
}
};
+ let source_map = source_map_from_code(&transpiled_code);
let coverage_report = generate_coverage_report(
&script_coverage,
- &transpiled_code,
- &source_map_from_code(&transpiled_code),
+ transpiled_code.take_as_string(),
+ &source_map,
&out_mode,
);
if !coverage_report.found_lines.is_empty() {
- reporter.report(&coverage_report, original_source)?;
+ reporter.report(&coverage_report, &original_source)?;
}
}
diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs
index 2f2015542..791aa6409 100644
--- a/cli/tsc/mod.rs
+++ b/cli/tsc/mod.rs
@@ -824,9 +824,9 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
});
runtime
- .execute_script(&located_script_name!(), startup_source)
+ .execute_script(located_script_name!(), startup_source)
.context("Could not properly start the compiler runtime.")?;
- runtime.execute_script(&located_script_name!(), &exec_source)?;
+ runtime.execute_script(located_script_name!(), exec_source)?;
let op_state = runtime.op_state();
let mut op_state = op_state.borrow_mut();
diff --git a/cli/util/text_encoding.rs b/cli/util/text_encoding.rs
index 87067e909..0111ec82f 100644
--- a/cli/util/text_encoding.rs
+++ b/cli/util/text_encoding.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+use deno_core::ModuleCode;
use encoding_rs::*;
use std::borrow::Cow;
use std::io::Error;
@@ -53,11 +54,12 @@ pub fn strip_bom(text: &str) -> &str {
}
}
-static SOURCE_MAP_PREFIX: &str =
- "//# sourceMappingURL=data:application/json;base64,";
+static SOURCE_MAP_PREFIX: &[u8] =
+ b"//# sourceMappingURL=data:application/json;base64,";
-pub fn source_map_from_code(code: &str) -> Option<Vec<u8>> {
- let last_line = code.rsplit(|u| u == '\n').next()?;
+pub fn source_map_from_code(code: &ModuleCode) -> Option<Vec<u8>> {
+ let bytes = code.as_bytes();
+ let last_line = bytes.rsplit(|u| *u == b'\n').next()?;
if last_line.starts_with(SOURCE_MAP_PREFIX) {
let input = last_line.split_at(SOURCE_MAP_PREFIX.len()).1;
let decoded_map = base64::decode(input)
@@ -68,17 +70,18 @@ pub fn source_map_from_code(code: &str) -> Option<Vec<u8>> {
}
}
-pub fn code_without_source_map(mut code: String) -> String {
- if let Some(last_line_index) = code.rfind('\n') {
- if code[last_line_index + 1..].starts_with(SOURCE_MAP_PREFIX) {
- code.truncate(last_line_index + 1);
- code
- } else {
- code
+/// Truncate the source code before the source map.
+pub fn code_without_source_map(mut code: ModuleCode) -> ModuleCode {
+ let bytes = code.as_bytes();
+ for i in (0..bytes.len()).rev() {
+ if bytes[i] == b'\n' {
+ if bytes[i + 1..].starts_with(SOURCE_MAP_PREFIX) {
+ code.truncate(i + 1);
+ }
+ return code;
}
- } else {
- code
}
+ code
}
#[cfg(test)]
@@ -155,8 +158,11 @@ mod tests {
"\n",
);
- fn run_test(input: &str, output: &str) {
- assert_eq!(code_without_source_map(input.to_string()), output);
+ fn run_test(input: &'static str, output: &'static str) {
+ assert_eq!(
+ code_without_source_map(input.into()).take_as_string(),
+ output
+ );
}
}
}
diff --git a/cli/worker.rs b/cli/worker.rs
index c505516a0..a0168a1f3 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -78,7 +78,7 @@ impl CliMainWorker {
self.execute_main_module_possibly_with_npm().await?;
}
- self.worker.dispatch_load_event(&located_script_name!())?;
+ self.worker.dispatch_load_event(located_script_name!())?;
loop {
self
@@ -87,13 +87,13 @@ impl CliMainWorker {
.await?;
if !self
.worker
- .dispatch_beforeunload_event(&located_script_name!())?
+ .dispatch_beforeunload_event(located_script_name!())?
{
break;
}
}
- self.worker.dispatch_unload_event(&located_script_name!())?;
+ self.worker.dispatch_unload_event(located_script_name!())?;
if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
self
@@ -129,7 +129,7 @@ impl CliMainWorker {
self
.inner
.worker
- .dispatch_load_event(&located_script_name!())?;
+ .dispatch_load_event(located_script_name!())?;
self.pending_unload = true;
let result = loop {
@@ -140,7 +140,7 @@ impl CliMainWorker {
match self
.inner
.worker
- .dispatch_beforeunload_event(&located_script_name!())
+ .dispatch_beforeunload_event(located_script_name!())
{
Ok(default_prevented) if default_prevented => {} // continue loop
Ok(_) => break Ok(()),
@@ -154,7 +154,7 @@ impl CliMainWorker {
self
.inner
.worker
- .dispatch_unload_event(&located_script_name!())?;
+ .dispatch_unload_event(located_script_name!())?;
Ok(())
}
@@ -166,7 +166,7 @@ impl CliMainWorker {
let _ = self
.inner
.worker
- .dispatch_unload_event(&located_script_name!());
+ .dispatch_unload_event(located_script_name!());
}
}
}
@@ -185,7 +185,7 @@ impl CliMainWorker {
// failures.
if self.ps.options.trace_ops() {
self.worker.js_runtime.execute_script(
- &located_script_name!(),
+ located_script_name!(),
"Deno[Deno.internal].core.enableOpCallTracing();",
)?;
}
@@ -200,19 +200,19 @@ impl CliMainWorker {
self.execute_side_module_possibly_with_npm().await?;
}
- self.worker.dispatch_load_event(&located_script_name!())?;
+ self.worker.dispatch_load_event(located_script_name!())?;
self.run_tests(&self.ps.options.shuffle_tests()).await?;
loop {
if !self
.worker
- .dispatch_beforeunload_event(&located_script_name!())?
+ .dispatch_beforeunload_event(located_script_name!())?
{
break;
}
self.worker.run_event_loop(false).await?;
}
- self.worker.dispatch_unload_event(&located_script_name!())?;
+ self.worker.dispatch_unload_event(located_script_name!())?;
if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
self
@@ -230,7 +230,7 @@ impl CliMainWorker {
self.enable_test();
self.worker.execute_script(
- &located_script_name!(),
+ located_script_name!(),
"Deno[Deno.internal].core.enableOpCallTracing();",
)?;
@@ -239,18 +239,18 @@ impl CliMainWorker {
self.execute_side_module_possibly_with_npm().await?;
}
- self.worker.dispatch_load_event(&located_script_name!())?;
+ self.worker.dispatch_load_event(located_script_name!())?;
self.run_tests(&None).await?;
loop {
if !self
.worker
- .dispatch_beforeunload_event(&located_script_name!())?
+ .dispatch_beforeunload_event(located_script_name!())?
{
break;
}
self.worker.run_event_loop(false).await?;
}
- self.worker.dispatch_unload_event(&located_script_name!())?;
+ self.worker.dispatch_unload_event(located_script_name!())?;
Ok(())
}
@@ -260,18 +260,18 @@ impl CliMainWorker {
// We execute the module module as a side module so that import.meta.main is not set.
self.execute_side_module_possibly_with_npm().await?;
- self.worker.dispatch_load_event(&located_script_name!())?;
+ self.worker.dispatch_load_event(located_script_name!())?;
self.run_benchmarks().await?;
loop {
if !self
.worker
- .dispatch_beforeunload_event(&located_script_name!())?
+ .dispatch_beforeunload_event(located_script_name!())?
{
break;
}
self.worker.run_event_loop(false).await?;
}
- self.worker.dispatch_unload_event(&located_script_name!())?;
+ self.worker.dispatch_unload_event(located_script_name!())?;
Ok(())
}