Skip to content

Commit 251e6bd

Browse files
authored
perf(turbopack): Migrate as many data structures to FxHash as possible (#75546)
Migrate as many things as possible that I could find with: ``` rg '\b(Index|Hash)(Map|Set)<' ``` # Benchmarks I tried building next-site, differences are likely within noise threshold. ## Before this change ``` $ hyperfine --warmup 1 --runs 5 --prepare 'rm -rf .next' 'TURBOPACK=1 TURBOPACK_BUILD=1 TURBO_ENGINE_READ_ONLY=1 pnpm next build --experimental-build-mode=compile' Benchmark 1: TURBOPACK=1 TURBOPACK_BUILD=1 TURBO_ENGINE_READ_ONLY=1 pnpm next build --experimental-build-mode=compile Time (mean ± σ): 27.254 s ± 0.268 s [User: 125.055 s, System: 16.090 s] Range (min … max): 26.810 s … 27.529 s 5 runs ``` ## After this change ``` $ hyperfine --warmup 1 --runs 5 --prepare 'rm -rf .next' 'TURBOPACK=1 TURBOPACK_BUILD=1 TURBO_ENGINE_READ_ONLY=1 pnpm next build --experimental-build-mode=compile' Benchmark 1: TURBOPACK=1 TURBOPACK_BUILD=1 TURBO_ENGINE_READ_ONLY=1 pnpm next build --experimental-build-mode=compile Time (mean ± σ): 26.711 s ± 0.461 s [User: 123.910 s, System: 15.667 s] Range (min … max): 26.346 s … 27.463 s 5 runs ```
1 parent 883147a commit 251e6bd

File tree

98 files changed

+474
-460
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+474
-460
lines changed

Cargo.lock

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/napi/src/next_api/utils.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
use std::{
2-
collections::HashMap, future::Future, ops::Deref, path::PathBuf, sync::Arc, time::Duration,
3-
};
1+
use std::{future::Future, ops::Deref, path::PathBuf, sync::Arc, time::Duration};
42

53
use anyhow::{anyhow, Context, Result};
64
use napi::{
75
bindgen_prelude::{External, ToNapiValue},
86
threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode},
97
JsFunction, JsObject, JsUnknown, NapiRaw, NapiValue, Status,
108
};
9+
use rustc_hash::FxHashMap;
1110
use serde::Serialize;
1211
use turbo_tasks::{
1312
task_statistics::TaskStatisticsApi, trace::TraceRawVcs, OperationVc, ReadRef, TaskId,
@@ -412,7 +411,8 @@ impl From<SourcePos> for NapiSourcePos {
412411
pub struct NapiDiagnostic {
413412
pub category: String,
414413
pub name: String,
415-
pub payload: HashMap<String, String>,
414+
#[napi(ts_type = "Record<string, string>")]
415+
pub payload: FxHashMap<String, String>,
416416
}
417417

418418
impl NapiDiagnostic {

crates/next-api/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ indexmap = { workspace = true }
2020
next-core = { workspace = true }
2121
petgraph = { workspace = true, features = ["serde-1"]}
2222
regex = { workspace = true }
23+
rustc-hash = { workspace = true }
2324
serde = { workspace = true }
2425
serde_json = { workspace = true }
2526
shadow-rs = { workspace = true }
27+
swc_core = { workspace = true }
2628
tracing = { workspace = true }
2729
turbo-rcstr = { workspace = true }
2830
turbo-tasks = { workspace = true }
@@ -34,11 +36,10 @@ turbopack = { workspace = true }
3436
turbopack-browser = { workspace = true }
3537
turbopack-cli-utils = { workspace = true }
3638
turbopack-core = { workspace = true }
37-
turbopack-env = { workspace = true }
3839
turbopack-ecmascript = { workspace = true }
40+
turbopack-env = { workspace = true }
3941
turbopack-node = { workspace = true }
4042
turbopack-nodejs = { workspace = true }
41-
swc_core = { workspace = true }
4243

4344
[build-dependencies]
4445
# It is not a mistake this dependency is specified in dep / build-dep both.

crates/next-api/src/client_references.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use std::collections::HashMap;
2-
31
use anyhow::Result;
42
use next_core::{
53
self,
64
next_client_reference::{CssClientReferenceModule, EcmascriptClientReferenceModule},
75
next_server_component::server_component_module::NextServerComponentModule,
86
};
7+
use rustc_hash::FxHashMap;
98
use serde::{Deserialize, Serialize};
109
use turbo_tasks::{
1110
debug::ValueDebugFormat, trace::TraceRawVcs, NonLocalValue, ResolvedVc, TryFlatJoinIterExt, Vc,
@@ -26,7 +25,7 @@ pub enum ClientReferenceMapType {
2625
}
2726

2827
#[turbo_tasks::value(transparent)]
29-
pub struct ClientReferencesSet(HashMap<ResolvedVc<Box<dyn Module>>, ClientReferenceMapType>);
28+
pub struct ClientReferencesSet(FxHashMap<ResolvedVc<Box<dyn Module>>, ClientReferenceMapType>);
3029

3130
#[turbo_tasks::function]
3231
pub async fn map_client_references(

crates/next-api/src/loadable_manifest.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use std::collections::HashMap;
2-
31
use anyhow::Result;
42
use next_core::{next_manifests::LoadableManifest, util::NextRuntime};
3+
use rustc_hash::FxHashMap;
54
use turbo_rcstr::RcStr;
65
use turbo_tasks::{ResolvedVc, TryFlatJoinIterExt, ValueToString, Vc};
76
use turbo_tasks_fs::{File, FileContent, FileSystemPath};
@@ -23,7 +22,7 @@ pub async fn create_react_loadable_manifest(
2322
) -> Result<Vc<OutputAssets>> {
2423
let dynamic_import_entries = &*dynamic_import_entries.await?;
2524

26-
let mut loadable_manifest: HashMap<RcStr, LoadableManifest> = Default::default();
25+
let mut loadable_manifest: FxHashMap<RcStr, LoadableManifest> = FxHashMap::default();
2726

2827
for (_, (module_id, chunk_output)) in dynamic_import_entries.into_iter() {
2928
let chunk_output = chunk_output.await?;

crates/next-api/src/module_graph.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{borrow::Cow, collections::HashMap};
1+
use std::borrow::Cow;
22

33
use anyhow::Result;
44
use next_core::{
@@ -9,6 +9,7 @@ use next_core::{
99
next_dynamic::NextDynamicEntryModule,
1010
next_manifests::ActionLayer,
1111
};
12+
use rustc_hash::FxHashMap;
1213
use tracing::Instrument;
1314
use turbo_tasks::{
1415
CollectiblesSource, FxIndexMap, FxIndexSet, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, Vc,
@@ -58,8 +59,8 @@ impl NextDynamicGraph {
5859
// This would clone the graph and allow changing the node weights. We can probably get away
5960
// with keeping the sidecar information separate from the graph itself, though.
6061
//
61-
// let mut reduced_modules: HashMap<Vc<Box<dyn Module>>, NodeIndex<u32>> =
62-
// HashMap::new(); let mut reduced_graph = DiGraph::new();
62+
// let mut reduced_modules: FxHashMap<Vc<Box<dyn Module>>, NodeIndex<u32>> =
63+
// FxHashMap::default(); let mut reduced_graph = DiGraph::new();
6364
// for idx in graph.node_indices() {
6465
// let weight = *graph.node_weight(idx).unwrap();
6566
// let new_idx = reduced_graph.add_node(weight);
@@ -112,7 +113,7 @@ impl NextDynamicGraph {
112113
let mut result = vec![];
113114

114115
// module -> the client reference entry (if any)
115-
let mut state_map = HashMap::new();
116+
let mut state_map = FxHashMap::default();
116117
graph.traverse_edges_from_entries(entries, |parent_info, node| {
117118
let module = node.module;
118119
let Some((parent_node, _)) = parent_info else {
@@ -210,7 +211,7 @@ impl ServerActionsGraph {
210211
return Ok(Vc::cell(Default::default()));
211212
}
212213

213-
let mut result = HashMap::new();
214+
let mut result = FxHashMap::default();
214215
graph.traverse_from_entry(entry, |node| {
215216
if let Some(node_data) = data.get(&node.module) {
216217
result.insert(node.module, *node_data);
@@ -309,7 +310,7 @@ impl ClientReferencesGraph {
309310
graph.traverse_edges_from_entries_topological(
310311
entries,
311312
// state_map is `module -> Option< the current so parent server component >`
312-
&mut HashMap::new(),
313+
&mut FxHashMap::default(),
313314
|parent_info, node, state_map| {
314315
let module = node.module;
315316
let module_type = data.get(&module);

crates/next-api/src/operation.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
use anyhow::Result;
2-
use indexmap::IndexMap;
32
use serde::{Deserialize, Serialize};
43
use turbo_rcstr::RcStr;
54
use turbo_tasks::{
6-
debug::ValueDebugFormat, get_effects, trace::TraceRawVcs, CollectiblesSource, NonLocalValue,
7-
OperationVc, ResolvedVc, Vc,
5+
debug::ValueDebugFormat, get_effects, trace::TraceRawVcs, CollectiblesSource, FxIndexMap,
6+
NonLocalValue, OperationVc, ResolvedVc, Vc,
87
};
98
use turbopack_core::{diagnostics::Diagnostic, issue::IssueDescriptionExt};
109

@@ -23,7 +22,7 @@ use crate::{
2322
/// This is needed to call `write_to_disk` which expects an `OperationVc<Endpoint>`.
2423
#[turbo_tasks::value(shared)]
2524
pub struct EntrypointsOperation {
26-
pub routes: IndexMap<RcStr, RouteOperation>,
25+
pub routes: FxIndexMap<RcStr, RouteOperation>,
2726
pub middleware: Option<MiddlewareOperation>,
2827
pub instrumentation: Option<InstrumentationOperation>,
2928
pub pages_document_endpoint: OperationVc<Box<dyn Endpoint>>,

crates/next-api/src/server_actions.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use std::{
2-
collections::{BTreeMap, HashMap},
3-
io::Write,
4-
};
1+
use std::{collections::BTreeMap, io::Write};
52

63
use anyhow::{bail, Context, Result};
74
use next_core::{
@@ -10,6 +7,7 @@ use next_core::{
107
},
118
util::NextRuntime,
129
};
10+
use rustc_hash::FxHashMap;
1311
use swc_core::{
1412
atoms::Atom,
1513
common::comments::Comments,
@@ -399,7 +397,7 @@ struct OptionActionMap(Option<ResolvedVc<ActionMap>>);
399397
type LayerAndActions = (ActionLayer, ResolvedVc<ActionMap>);
400398
/// A mapping of every module module containing Server Actions, mapping to its layer and actions.
401399
#[turbo_tasks::value(transparent)]
402-
pub struct AllModuleActions(HashMap<ResolvedVc<Box<dyn Module>>, LayerAndActions>);
400+
pub struct AllModuleActions(FxHashMap<ResolvedVc<Box<dyn Module>>, LayerAndActions>);
403401

404402
#[turbo_tasks::function]
405403
pub async fn map_server_actions(graph: Vc<SingleModuleGraph>) -> Result<Vc<AllModuleActions>> {

crates/next-api/src/versioned_content_map.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use std::collections::{HashMap, HashSet};
2-
31
use anyhow::{bail, Result};
42
use next_core::emit_assets;
3+
use rustc_hash::{FxHashMap, FxHashSet};
54
use serde::{Deserialize, Serialize};
65
use turbo_rcstr::RcStr;
76
use turbo_tasks::{
@@ -31,7 +30,7 @@ use turbopack_core::{
3130
struct MapEntry {
3231
assets_operation: OperationVc<OutputAssets>,
3332
/// Precomputed map for quick access to output asset by filepath
34-
path_to_asset: HashMap<ResolvedVc<FileSystemPath>, ResolvedVc<Box<dyn OutputAsset>>>,
33+
path_to_asset: FxHashMap<ResolvedVc<FileSystemPath>, ResolvedVc<Box<dyn OutputAsset>>>,
3534
}
3635

3736
// HACK: This is technically incorrect because `path_to_asset` contains `ResolvedVc`...
@@ -49,15 +48,15 @@ pub struct PathToOutputOperation(
4948
/// It may not be 100% correct for the key (`FileSystemPath`) to be in a `ResolvedVc` here, but
5049
/// it's impractical to make it an `OperationVc`/`OperationValue`, and it's unlikely to
5150
/// change/break?
52-
HashMap<ResolvedVc<FileSystemPath>, FxIndexSet<OperationVc<OutputAssets>>>,
51+
FxHashMap<ResolvedVc<FileSystemPath>, FxIndexSet<OperationVc<OutputAssets>>>,
5352
);
5453

5554
// HACK: This is technically incorrect because the map's key is a `ResolvedVc`...
5655
unsafe impl OperationValue for PathToOutputOperation {}
5756

5857
// A precomputed map for quick access to output asset by filepath
5958
type OutputOperationToComputeEntry =
60-
HashMap<OperationVc<OutputAssets>, OperationVc<OptionMapEntry>>;
59+
FxHashMap<OperationVc<OutputAssets>, OperationVc<OptionMapEntry>>;
6160

6261
#[turbo_tasks::value]
6362
pub struct VersionedContentMap {
@@ -77,8 +76,8 @@ impl VersionedContentMap {
7776
// should be a singleton for each project.
7877
pub fn new() -> ResolvedVc<Self> {
7978
VersionedContentMap {
80-
map_path_to_op: State::new(PathToOutputOperation(HashMap::new())),
81-
map_op_to_compute_entry: State::new(HashMap::new()),
79+
map_path_to_op: State::new(PathToOutputOperation(FxHashMap::default())),
80+
map_op_to_compute_entry: State::new(FxHashMap::default()),
8281
}
8382
.resolved_cell()
8483
}
@@ -142,7 +141,7 @@ impl VersionedContentMap {
142141
let mut changed = false;
143142

144143
// get current map's keys, subtract keys that don't exist in operation
145-
let mut stale_assets = map.0.keys().copied().collect::<HashSet<_>>();
144+
let mut stale_assets = map.0.keys().copied().collect::<FxHashSet<_>>();
146145

147146
for (k, _) in entries.iter() {
148147
let res = map.0.entry(*k).or_default().insert(assets_operation);

0 commit comments

Comments
 (0)