Repository: denoland/deno
Pull request: perf: parse source files in parallel

See https://github.com/denoland/deno_graph/pull/477

This is much faster on first run, but the same speed on second run (due to the caches).

First run (on mac m2, 8 cores):

```
% DENO_DIR=$PWD/.cache hyperfine --warmup=3 --prepare="rm -rf .cache/gen .cache/dep_analysis_cache*" "../deno/deno_old run -A main.ts" "../deno/target/release/deno run -A main.ts"
Benchmark 1: ../deno/deno_old run -A main.ts
  Time (mean ± σ):      2.265 s ±  0.013 s    [User: 2.145 s, System: 0.143 s]
  Range (min … max):    2.239 s …  2.283 s    10 runs
 
Benchmark 2: ../deno/target/release/deno run -A main.ts
  Time (mean ± σ):     943.2 ms ±   5.2 ms    [User: 2230.1 ms, System: 146.9 ms]
  Range (min … max):   934.5 ms … 950.2 ms    10 runs
 
Summary
  ../deno/target/release/deno run -A main.ts ran
    2.40 ± 0.02 times faster than ../deno/deno_old run -A main.ts
scratch % cat main.ts
import "https://deno.land/x/ts_morph@22.0.0/mod.ts";
import "https://deno.land/x/ts_morph@21.0.0/mod.ts";
import "https://deno.land/x/ts_morph@20.0.0/mod.ts";
import "https://deno.land/x/ts_morph@19.0.0/mod.ts";
```

Changed files:
filename: cli/cache/module_info.rs
patch: @@ -7,14 +7,14 @@ use deno_ast::ModuleSpecifier;
 use deno_core::error::AnyError;
 use deno_core::serde_json;
 use deno_graph::ModuleInfo;
-use deno_graph::ModuleParser;
 use deno_graph::ParserModuleAnalyzer;
 use deno_runtime::deno_webstorage::rusqlite::params;
 
 use super::cache_db::CacheDB;
 use super::cache_db::CacheDBConfiguration;
 use super::cache_db::CacheFailure;
 use super::FastInsecureHasher;
+use super::ParsedSourceCache;
 
 const SELECT_MODULE_INFO: &str = "
 SELECT
@@ -136,18 +136,18 @@ impl ModuleInfoCache {
 
   pub fn as_module_analyzer<'a>(
     &'a self,
-    parser: &'a dyn ModuleParser,
+    parsed_source_cache: &'a Arc<ParsedSourceCache>,
   ) -> ModuleInfoCacheModuleAnalyzer<'a> {
     ModuleInfoCacheModuleAnalyzer {
       module_info_cache: self,
-      parser,
+      parsed_source_cache,
     }
   }
 }
 
 pub struct ModuleInfoCacheModuleAnalyzer<'a> {
   module_info_cache: &'a ModuleInfoCache,
-  parser: &'a dyn ModuleParser,
+  parsed_source_cache: &'a Arc<ParsedSourceCache>,
 }
 
 #[async_trait::async_trait(?Send)]
@@ -177,9 +177,17 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
     }
 
     // otherwise, get the module info from the parsed source cache
-    // todo(23858): take advantage of this being async
-    let analyzer = ParserModuleAnalyzer::new(self.parser);
-    let module_info = analyzer.analyze(specifier, source, media_type).await?;
+    let module_info = deno_core::unsync::spawn_blocking({
+      let cache = self.parsed_source_cache.clone();
+      let specifier = specifier.clone();
+      move || {
+        let parser = cache.as_capturing_parser();
+        let analyzer = ParserModuleAnalyzer::new(&parser);
+        analyzer.analyze_sync(&specifier, source, media_type)
+      }
+    })
+    .await
+    .unwrap()?;
 
     // then attempt to cache it
     if let Err(err) = self.module_info_cache.set_module_info(

filename: cli/graph_util.rs
patch: @@ -470,8 +470,9 @@ impl ModuleGraphBuilder {
     }
 
     let maybe_imports = self.options.to_maybe_imports()?;
-    let parser = self.parsed_source_cache.as_capturing_parser();
-    let analyzer = self.module_info_cache.as_module_analyzer(&parser);
+    let analyzer = self
+      .module_info_cache
+      .as_module_analyzer(&self.parsed_source_cache);
     let mut loader = match options.loader {
       Some(loader) => MutLoaderRef::Borrowed(loader),
       None => MutLoaderRef::Owned(self.create_graph_loader()),

filename: cli/tools/doc.rs
patch: @@ -89,7 +89,7 @@ pub async fn doc(flags: Flags, doc_flags: DocFlags) -> Result<(), AnyError> {
   let module_info_cache = factory.module_info_cache()?;
   let parsed_source_cache = factory.parsed_source_cache();
   let capturing_parser = parsed_source_cache.as_capturing_parser();
-  let analyzer = module_info_cache.as_module_analyzer(&capturing_parser);
+  let analyzer = module_info_cache.as_module_analyzer(parsed_source_cache);
 
   let doc_nodes_by_url = match doc_flags.source_files {
     DocSourceFileFlag::Builtin => {