Skip to content

Commit

Permalink
refactor: module hash (#7569)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahabhgk authored Aug 15, 2024
1 parent 14dce1a commit a5e3212
Show file tree
Hide file tree
Showing 96 changed files with 1,477 additions and 1,124 deletions.
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

179 changes: 79 additions & 100 deletions crates/rspack_core/src/chunk_graph/chunk_graph_module.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
//! There are methods whose verb is `ChunkGraphModule`
use std::hash::Hasher;
use std::hash::{Hash, Hasher};

use rspack_collections::{IdentifierMap, UkeySet};
use rspack_collections::{IdentifierSet, UkeySet};
use rspack_hash::RspackHashDigest;
use rspack_util::ext::DynHash;
use rustc_hash::FxHasher;
use tracing::instrument;

use crate::update_hash::{UpdateHashContext, UpdateRspackHash};
use crate::ChunkGraph;
use crate::{
get_chunk_group_from_ukey, AsyncDependenciesBlockIdentifier, BoxModule, ChunkByUkey, ChunkGroup,
ChunkGroupByUkey, ChunkGroupUkey, ChunkUkey, Compilation, ExportsHash, ModuleIdentifier,
RuntimeGlobals, RuntimeSpec, RuntimeSpecMap, RuntimeSpecSet,
get_chunk_group_from_ukey, AsyncDependenciesBlockIdentifier, ChunkByUkey, ChunkGroup,
ChunkGroupByUkey, ChunkGroupUkey, ChunkUkey, Compilation, ModuleIdentifier, RuntimeGlobals,
RuntimeSpec, RuntimeSpecMap, RuntimeSpecSet,
};
use crate::{ChunkGraph, Module};

#[derive(Debug, Clone, Default)]
pub struct ChunkGraphModule {
Expand All @@ -21,7 +22,7 @@ pub struct ChunkGraphModule {
pub chunks: UkeySet<ChunkUkey>,
pub(crate) runtime_requirements: Option<RuntimeSpecMap<RuntimeGlobals>>,
pub(crate) runtime_in_chunks: UkeySet<ChunkUkey>,
// pub(crate) hashes: Option<RuntimeSpecMap<u64>>,
pub(crate) hashes: Option<RuntimeSpecMap<RspackHashDigest>>,
}

impl ChunkGraphModule {
Expand All @@ -32,7 +33,7 @@ impl ChunkGraphModule {
chunks: Default::default(),
runtime_requirements: None,
runtime_in_chunks: Default::default(),
// hashes: None,
hashes: None,
}
}
}
Expand Down Expand Up @@ -165,106 +166,84 @@ impl ChunkGraph {
self.block_to_chunk_group_ukey.insert(block, chunk_group);
}

pub fn get_module_hash(
&self,
module_identifier: ModuleIdentifier,
runtime: &RuntimeSpec,
) -> Option<&RspackHashDigest> {
let cgm = self.get_chunk_graph_module(module_identifier);
if let Some(hashes) = &cgm.hashes {
if let Some(hash) = hashes.get(runtime) {
return Some(hash);
}
}
None
}

pub fn set_module_hashes(
&mut self,
module_identifier: ModuleIdentifier,
hashes: RuntimeSpecMap<RspackHashDigest>,
) {
let cgm = self.get_chunk_graph_module_mut(module_identifier);
cgm.hashes = Some(hashes);
}

#[instrument(name = "chunk_graph:get_module_graph_hash", skip_all)]
pub fn get_module_graph_hash(
&self,
module: &BoxModule,
module: &dyn Module,
compilation: &Compilation,
runtime: Option<&RuntimeSpec>,
with_connections: bool,
) -> String {
) -> u64 {
let mut hasher = FxHasher::default();
let mut connection_hash_cache: IdentifierMap<u64> = IdentifierMap::default();
let module_graph = &compilation.get_module_graph();

let process_module_graph_module = |module: &BoxModule, strict: Option<bool>| -> u64 {
let mut hasher = FxHasher::default();
module.identifier().dyn_hash(&mut hasher);
module.source_types().dyn_hash(&mut hasher);
module_graph
.is_async(&module.identifier())
.dyn_hash(&mut hasher);

module_graph
.get_exports_info(&module.identifier())
.export_info_hash(&mut hasher, module_graph, &mut UkeySet::default());

module
.get_blocks()
.iter()
.filter_map(|id| module_graph.block_by_id(id))
.for_each(|block| {
block.update_hash(
&mut hasher,
&UpdateHashContext {
compilation,
runtime,
},
)
});

// NOTE:
// Webpack use module.getExportsType() to generate hash
// but the module graph may be modified in it
// and exports type is calculated from build meta and exports info
// so use them to generate hash directly to avoid mutable access to module graph
if let Some(strict) = strict {
if let Some(build_meta) = module.build_meta() {
strict.dyn_hash(&mut hasher);
build_meta.default_object.dyn_hash(&mut hasher);
build_meta.exports_type.dyn_hash(&mut hasher);
}
}

hasher.finish()
};

// hash module build_info
module_graph
.get_module_hash(&module.identifier())
.dyn_hash(&mut hasher);
// hash module graph module
process_module_graph_module(module, None).dyn_hash(&mut hasher);

let strict: bool = module_graph
.module_by_identifier(&module.identifier())
.unwrap_or_else(|| {
panic!(
"Module({}) should be added before using",
module.identifier()
)
})
.get_strict_harmony_module();

if with_connections {
let mut connections = module_graph
.get_outgoing_connections(&module.identifier())
.into_iter()
.collect::<Vec<_>>();

connections.sort_by(|a, b| a.module_identifier().cmp(b.module_identifier()));

// hash connection module graph modules
self
.get_module_graph_hash_without_connections(module, compilation, runtime)
.hash(&mut hasher);
let strict = module.get_strict_harmony_module();
let mg = compilation.get_module_graph();
let connections = mg
.get_outgoing_connections(&module.identifier())
.into_iter()
.collect::<Vec<_>>();
if !connections.is_empty() {
let mut visited_modules = IdentifierSet::default();
visited_modules.insert(module.identifier());
for connection in connections {
if let Some(connection_hash) = connection_hash_cache.get(connection.module_identifier()) {
connection_hash.dyn_hash(&mut hasher)
} else {
let connection_hash = process_module_graph_module(
module_graph
.module_by_identifier(connection.module_identifier())
.unwrap_or_else(|| {
panic!(
"Module({}) should be added before using",
connection.module_identifier()
)
}),
Some(strict),
);
connection_hash.dyn_hash(&mut hasher);
connection_hash_cache.insert(*connection.module_identifier(), connection_hash);
let module_identifier = connection.module_identifier();
if visited_modules.contains(module_identifier) {
continue;
}
if connection.get_active_state(&mg, runtime).is_false() {
continue;
}
visited_modules.insert(*module_identifier);
let module = mg
.module_by_identifier(module_identifier)
.expect("should have module")
.as_ref();
module.get_exports_type(&mg, strict).hash(&mut hasher);
self.get_module_graph_hash_without_connections(module, compilation, runtime);
}
}
hasher.finish()
}

format!("{:016x}", hasher.finish())
fn get_module_graph_hash_without_connections(
&self,
module: &dyn Module,
compilation: &Compilation,
runtime: Option<&RuntimeSpec>,
) -> u64 {
let mut hasher = FxHasher::default();
let mg = compilation.get_module_graph();
let module_identifier = module.identifier();
let cgm = self.get_chunk_graph_module(module_identifier);
cgm.id.as_ref().dyn_hash(&mut hasher);
module.source_types().dyn_hash(&mut hasher);
mg.is_async(&module_identifier).dyn_hash(&mut hasher);
mg.get_exports_info(&module_identifier)
.update_hash(&mg, &mut hasher, compilation, runtime);
hasher.finish()
}
}
8 changes: 8 additions & 0 deletions crates/rspack_core/src/code_generation_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,11 @@ impl CodeGenerationResults {
code_generation_result.hash.as_ref()
}
}

#[derive(Debug)]
pub struct CodeGenerationJob {
pub module: ModuleIdentifier,
pub hash: RspackHashDigest,
pub runtime: RuntimeSpec,
pub runtimes: Vec<RuntimeSpec>,
}
Loading

2 comments on commit a5e3212

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ran ecosystem CI: Open

suite result
modernjs ✅ success
_selftest ✅ success
nx ❌ failure
rspress ✅ success
rsbuild ✅ success
examples ✅ success

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

Name Base (2024-08-15 5271a8b) Current Change
10000_development-mode + exec 2.31 s ± 16 ms 2.38 s ± 22 ms +2.75 %
10000_development-mode_hmr + exec 697 ms ± 6.7 ms 725 ms ± 17 ms +3.93 %
10000_production-mode + exec 2.85 s ± 37 ms 3.05 s ± 36 ms +6.81 %
arco-pro_development-mode + exec 1.88 s ± 77 ms 1.92 s ± 68 ms +2.20 %
arco-pro_development-mode_hmr + exec 433 ms ± 2.7 ms 439 ms ± 1.6 ms +1.49 %
arco-pro_production-mode + exec 3.43 s ± 72 ms 3.53 s ± 68 ms +2.98 %
arco-pro_production-mode_generate-package-json-webpack-plugin + exec 3.49 s ± 75 ms 3.59 s ± 76 ms +2.79 %
threejs_development-mode_10x + exec 1.68 s ± 15 ms 1.73 s ± 17 ms +2.98 %
threejs_development-mode_10x_hmr + exec 797 ms ± 1.8 ms 829 ms ± 9.2 ms +3.99 %
threejs_production-mode_10x + exec 5.48 s ± 28 ms 5.56 s ± 37 ms +1.45 %

Threshold exceeded: ["10000_production-mode + exec"]

Please sign in to comment.