Skip to content

Commit 7917de9

Browse files
authored
perf: add precomputed-hash for CachedSource (#128)
* perf: add precomputed-cache for `CachedSource` * perf: case * perf: case
1 parent 1042459 commit 7917de9

File tree

3 files changed

+81
-12
lines changed

3 files changed

+81
-12
lines changed

benches/bench.rs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
#![allow(missing_docs)]
22

3+
use std::collections::HashMap;
4+
35
#[cfg(not(codspeed))]
46
pub use criterion::*;
57

68
#[cfg(codspeed)]
79
pub use codspeed_criterion_compat::*;
810

911
use rspack_sources::{
10-
CachedSource, ConcatSource, MapOptions, ReplaceSource, Source, SourceMap,
11-
SourceMapSource, SourceMapSourceOptions,
12+
BoxSource, CachedSource, ConcatSource, MapOptions, ReplaceSource, Source,
13+
SourceExt, SourceMap, SourceMapSource, SourceMapSourceOptions,
1214
};
1315

1416
const HELLOWORLD_JS: &str = include_str!(concat!(
@@ -195,6 +197,61 @@ fn benchmark_replace_large_minified_source(b: &mut Bencher) {
195197
});
196198
}
197199

200+
fn benchmark_concat_generate_string_with_cache_as_key(b: &mut Bencher) {
201+
let sms_minify = SourceMapSource::new(SourceMapSourceOptions {
202+
value: HELLOWORLD_MIN_JS,
203+
name: "helloworld.min.js",
204+
source_map: SourceMap::from_json(HELLOWORLD_MIN_JS_MAP).unwrap(),
205+
original_source: Some(HELLOWORLD_JS.to_string()),
206+
inner_source_map: Some(SourceMap::from_json(HELLOWORLD_JS_MAP).unwrap()),
207+
remove_original_source: false,
208+
});
209+
let sms_rollup = SourceMapSource::new(SourceMapSourceOptions {
210+
value: BUNDLE_JS,
211+
name: "bundle.js",
212+
source_map: SourceMap::from_json(BUNDLE_JS_MAP).unwrap(),
213+
original_source: None,
214+
inner_source_map: None,
215+
remove_original_source: false,
216+
});
217+
let concat = ConcatSource::new([sms_minify, sms_rollup]);
218+
let cached = CachedSource::new(concat).boxed();
219+
220+
b.iter(|| {
221+
let mut m = HashMap::<BoxSource, ()>::new();
222+
m.insert(cached.clone(), ());
223+
let _ = black_box(|| m.get(&cached));
224+
let _ = black_box(|| m.get(&cached));
225+
})
226+
}
227+
228+
fn benchmark_concat_generate_string_as_key(b: &mut Bencher) {
229+
let sms_minify = SourceMapSource::new(SourceMapSourceOptions {
230+
value: HELLOWORLD_MIN_JS,
231+
name: "helloworld.min.js",
232+
source_map: SourceMap::from_json(HELLOWORLD_MIN_JS_MAP).unwrap(),
233+
original_source: Some(HELLOWORLD_JS.to_string()),
234+
inner_source_map: Some(SourceMap::from_json(HELLOWORLD_JS_MAP).unwrap()),
235+
remove_original_source: false,
236+
});
237+
let sms_rollup = SourceMapSource::new(SourceMapSourceOptions {
238+
value: BUNDLE_JS,
239+
name: "bundle.js",
240+
source_map: SourceMap::from_json(BUNDLE_JS_MAP).unwrap(),
241+
original_source: None,
242+
inner_source_map: None,
243+
remove_original_source: false,
244+
});
245+
let concat = ConcatSource::new([sms_minify, sms_rollup]).boxed();
246+
247+
b.iter(|| {
248+
let mut m = HashMap::<BoxSource, ()>::new();
249+
m.insert(concat.clone(), ());
250+
let _ = black_box(|| m.get(&concat));
251+
let _ = black_box(|| m.get(&concat));
252+
})
253+
}
254+
198255
fn bench_rspack_sources(criterion: &mut Criterion) {
199256
let mut group = criterion.benchmark_group("rspack_sources");
200257
group.bench_function(
@@ -213,6 +270,14 @@ fn bench_rspack_sources(criterion: &mut Criterion) {
213270
"replace_large_minified_source",
214271
benchmark_replace_large_minified_source,
215272
);
273+
group.bench_function(
274+
"concat_generate_string_with_cache_as_key",
275+
benchmark_concat_generate_string_with_cache_as_key,
276+
);
277+
group.bench_function(
278+
"concat_generate_string_as_key",
279+
benchmark_concat_generate_string_as_key,
280+
);
216281
group.finish();
217282
}
218283

src/cached_source.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{
22
borrow::Cow,
3-
hash::{BuildHasherDefault, Hash},
3+
hash::{BuildHasherDefault, Hash, Hasher},
44
sync::{Arc, OnceLock},
55
};
66

@@ -52,6 +52,7 @@ pub struct CachedSource<T> {
5252
inner: Arc<T>,
5353
cached_buffer: Arc<OnceLock<Vec<u8>>>,
5454
cached_source: Arc<OnceLock<Arc<str>>>,
55+
cached_hash: Arc<OnceLock<u64>>,
5556
cached_maps:
5657
Arc<DashMap<MapOptions, Option<SourceMap>, BuildHasherDefault<FxHasher>>>,
5758
}
@@ -63,6 +64,7 @@ impl<T> CachedSource<T> {
6364
inner: Arc::new(inner),
6465
cached_buffer: Default::default(),
6566
cached_source: Default::default(),
67+
cached_hash: Default::default(),
6668
cached_maps: Default::default(),
6769
}
6870
}
@@ -89,11 +91,7 @@ impl<T: Source + Hash + PartialEq + Eq + 'static> Source for CachedSource<T> {
8991
}
9092

9193
fn size(&self) -> usize {
92-
let source = self.cached_source.get();
93-
match source {
94-
Some(source) => source.len(),
95-
None => self.inner.size(),
96-
}
94+
self.source().len()
9795
}
9896

9997
fn map(&self, options: &MapOptions) -> Option<SourceMap> {
@@ -160,20 +158,26 @@ impl<T: Source + Hash + PartialEq + Eq + 'static> StreamChunks<'_>
160158
}
161159
}
162160

163-
impl<T: Source> Clone for CachedSource<T> {
161+
impl<T> Clone for CachedSource<T> {
164162
fn clone(&self) -> Self {
165163
Self {
166164
inner: self.inner.clone(),
167165
cached_buffer: self.cached_buffer.clone(),
168166
cached_source: self.cached_source.clone(),
167+
cached_hash: self.cached_hash.clone(),
169168
cached_maps: self.cached_maps.clone(),
170169
}
171170
}
172171
}
173172

174-
impl<T: Hash> Hash for CachedSource<T> {
173+
impl<T: Source + Hash + PartialEq + Eq + 'static> Hash for CachedSource<T> {
175174
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
176-
self.inner.hash(state);
175+
(self.cached_hash.get_or_init(|| {
176+
let mut hasher = FxHasher::default();
177+
hasher.write(self.source().as_bytes());
178+
hasher.finish()
179+
}))
180+
.hash(state);
177181
}
178182
}
179183

src/source.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ mod tests {
522522
RawSource::from("g").boxed().hash(&mut state);
523523
(&RawSource::from("h") as &dyn Source).hash(&mut state);
524524
ReplaceSource::new(RawSource::from("i").boxed()).hash(&mut state);
525-
assert_eq!(format!("{:x}", state.finish()), "8163b42b7cb1d8f0");
525+
assert_eq!(format!("{:x}", state.finish()), "ef733b8b8ee61bf0");
526526
}
527527

528528
#[test]

0 commit comments

Comments
 (0)