From f981becf3e5e54e2a3a90583b8fe82cde51c3bed Mon Sep 17 00:00:00 2001 From: David Sherret Date: Sat, 1 Apr 2023 10:12:40 -0400 Subject: [PATCH] perf(check): faster source hashing (#18534) --- cli/tools/check.rs | 10 +++--- cli/tsc/99_main_compiler.js | 2 +- cli/tsc/mod.rs | 68 ++++++++++++++----------------------- 3 files changed, 32 insertions(+), 48 deletions(-) diff --git a/cli/tools/check.rs b/cli/tools/check.rs index 12f9d06b51..804519e84f 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -89,10 +89,12 @@ pub fn check( // to make tsc build info work, we need to consistently hash modules, so that // tsc can better determine if an emit is still valid or not, so we provide // that data here. - let hash_data = vec![ - options.ts_config.as_bytes(), - version::deno().as_bytes().to_owned(), - ]; + let hash_data = { + let mut hasher = FastInsecureHasher::new(); + hasher.write(&options.ts_config.as_bytes()); + hasher.write_str(version::deno()); + hasher.finish() + }; let response = tsc::exec(tsc::Request { config: options.ts_config, diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index 2f565770c4..56ac5522b0 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -625,7 +625,7 @@ delete Object.prototype.__proto__; } }, createHash(data) { - return ops.op_create_hash({ data }).hash; + return ops.op_create_hash(data); }, // LanguageServiceHost diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 85132b475d..a9dc5b7f32 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -2,6 +2,7 @@ use crate::args::TsConfig; use crate::args::TypeCheckMode; +use crate::cache::FastInsecureHasher; use crate::node; use crate::node::node_resolve_npm_reference; use crate::node::NodeResolution; @@ -241,15 +242,16 @@ fn get_lazily_loaded_asset(asset: &str) -> Option<&'static str> { fn get_maybe_hash( maybe_source: Option<&str>, - hash_data: &[Vec], + hash_data: u64, ) -> Option { - if let Some(source) = maybe_source { - let mut data = vec![source.as_bytes().to_owned()]; - data.extend_from_slice(hash_data); - Some(checksum::gen(&data)) - } else { - None - } + maybe_source.map(|source| get_hash(source, hash_data)) +} + +fn get_hash(source: &str, hash_data: u64) -> String { + let mut hasher = FastInsecureHasher::new(); + hasher.write_str(source); + hasher.write_u64(hash_data); + hasher.finish().to_string() } /// Hash the URL so it can be sent to `tsc` in a supportable way @@ -303,7 +305,7 @@ pub struct Request { /// Indicates to the tsc runtime if debug logging should occur. pub debug: bool, pub graph: Arc, - pub hash_data: Vec>, + pub hash_data: u64, pub maybe_npm_resolver: Option, pub maybe_tsbuildinfo: Option, /// A vector of strings that represent the root/entry point modules for the @@ -324,7 +326,7 @@ pub struct Response { #[derive(Debug, Default)] struct State { - hash_data: Vec>, + hash_data: u64, graph: Arc, maybe_tsbuildinfo: Option, maybe_response: Option, @@ -337,7 +339,7 @@ struct State { impl State { pub fn new( graph: Arc, - hash_data: Vec>, + hash_data: u64, maybe_npm_resolver: Option, maybe_tsbuildinfo: Option, root_map: HashMap, @@ -364,23 +366,10 @@ fn normalize_specifier( resolve_url_or_path(specifier, current_dir).map_err(|err| err.into()) } -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct CreateHashArgs { - /// The string data to be used to generate the hash. This will be mixed with - /// other state data in Deno to derive the final hash. - data: String, -} - #[op] -fn op_create_hash(s: &mut OpState, args: Value) -> Result { +fn op_create_hash(s: &mut OpState, text: &str) -> String { let state = s.borrow_mut::(); - let v: CreateHashArgs = serde_json::from_value(args) - .context("Invalid request from JavaScript for \"op_create_hash\".")?; - let mut data = vec![v.data.as_bytes().to_owned()]; - data.extend_from_slice(&state.hash_data); - let hash = checksum::gen(&data); - Ok(json!({ "hash": hash })) + get_hash(text, state.hash_data) } #[derive(Debug, Deserialize)] @@ -455,7 +444,7 @@ fn op_load(state: &mut OpState, args: Value) -> Result { Some(Cow::Borrowed("declare const __: any;\nexport = __;\n")) } else if let Some(name) = v.specifier.strip_prefix("asset:///") { let maybe_source = get_lazily_loaded_asset(name); - hash = get_maybe_hash(maybe_source, &state.hash_data); + hash = get_maybe_hash(maybe_source, state.hash_data); media_type = MediaType::from_str(&v.specifier); maybe_source.map(Cow::Borrowed) } else { @@ -507,7 +496,7 @@ fn op_load(state: &mut OpState, args: Value) -> Result { media_type = MediaType::Unknown; None }; - hash = get_maybe_hash(maybe_source.as_deref(), &state.hash_data); + hash = get_maybe_hash(maybe_source.as_deref(), state.hash_data); maybe_source }; @@ -902,12 +891,12 @@ mod tests { async fn setup( maybe_specifier: Option, - maybe_hash_data: Option>>, + maybe_hash_data: Option, maybe_tsbuildinfo: Option, ) -> OpState { let specifier = maybe_specifier .unwrap_or_else(|| ModuleSpecifier::parse("file:///main.ts").unwrap()); - let hash_data = maybe_hash_data.unwrap_or_else(|| vec![b"".to_vec()]); + let hash_data = maybe_hash_data.unwrap_or(0); let fixtures = test_util::testdata_path().join("tsc2"); let mut loader = MockLoader { fixtures }; let mut graph = ModuleGraph::default(); @@ -933,7 +922,7 @@ mod tests { async fn test_exec( specifier: &ModuleSpecifier, ) -> Result { - let hash_data = vec![b"something".to_vec()]; + let hash_data = 123; // something random let fixtures = test_util::testdata_path().join("tsc2"); let mut loader = MockLoader { fixtures }; let mut graph = ModuleGraph::default(); @@ -999,16 +988,9 @@ mod tests { #[tokio::test] async fn test_create_hash() { - let mut state = setup(None, Some(vec![b"something".to_vec()]), None).await; - let actual = op_create_hash::call( - &mut state, - json!({ "data": "some sort of content" }), - ) - .expect("could not invoke op"); - assert_eq!( - actual, - json!({"hash": "ae92df8f104748768838916857a1623b6a3c593110131b0a00f81ad9dac16511"}) - ); + let mut state = setup(None, Some(123), None).await; + let actual = op_create_hash::call(&mut state, "some sort of content"); + assert_eq!(actual, "11905938177474799758"); } #[test] @@ -1050,12 +1032,12 @@ mod tests { &mut state, json!({ "specifier": "https://deno.land/x/mod.ts"}), ) - .expect("should have invoked op"); + .unwrap(); assert_eq!( actual, json!({ "data": "console.log(\"hello deno\");\n", - "version": "149c777056afcc973d5fcbe11421b6d5ddc57b81786765302030d7fc893bf729", + "version": "7821807483407828376", "scriptKind": 3, }) );