diff --git a/CHANGELOG.md b/CHANGELOG.md index 14cc77dbfb59..7be15ceb683d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -- Nothing yet! +### Added + +- Parallelize parsing of individual source files ([#15270](https://github.com/tailwindlabs/tailwindcss/pull/15270)) ## [4.0.0-beta.4] - 2024-11-29 diff --git a/crates/oxide/src/lib.rs b/crates/oxide/src/lib.rs index a9567c5c4019..4c7621e7a1c6 100644 --- a/crates/oxide/src/lib.rs +++ b/crates/oxide/src/lib.rs @@ -102,13 +102,11 @@ impl Scanner { pub fn scan(&mut self) -> Vec { init_tracing(); self.prepare(); - self.check_for_new_files(); self.compute_candidates(); - let mut candidates: Vec = self.candidates.clone().into_iter().collect(); - - candidates.sort(); + let mut candidates: Vec = self.candidates.clone().into_par_iter().collect(); + candidates.par_sort(); candidates } @@ -140,7 +138,7 @@ impl Scanner { let extractor = Extractor::with_positions(&content[..], Default::default()); let candidates: Vec<(String, usize)> = extractor - .into_iter() + .into_par_iter() .map(|(s, i)| { // SAFETY: When we parsed the candidates, we already guaranteed that the byte slices // are valid, therefore we don't have to re-check here when we want to convert it back @@ -156,7 +154,7 @@ impl Scanner { self.prepare(); self.files - .iter() + .par_iter() .filter_map(|x| Path::from(x.clone()).canonicalize().ok()) .map(|x| x.to_string()) .collect() @@ -201,7 +199,7 @@ impl Scanner { if !changed_content.is_empty() { let candidates = parse_all_blobs(read_all_files(changed_content)); - self.candidates.extend(candidates); + self.candidates.par_extend(candidates); } } @@ -209,6 +207,7 @@ impl Scanner { // content for candidates. fn prepare(&mut self) { if self.ready { + self.check_for_new_files(); return; } @@ -455,12 +454,10 @@ fn read_all_files(changed_content: Vec) -> Vec> { #[tracing::instrument(skip_all)] fn parse_all_blobs(blobs: Vec>) -> Vec { - let input: Vec<_> = blobs.iter().map(|blob| &blob[..]).collect(); - let input = &input[..]; - - let mut result: Vec = input + let mut result: Vec<_> = blobs .par_iter() - .map(|input| Extractor::unique(input, Default::default())) + .flat_map(|blob| blob.par_split(|x| matches!(x, b'\n'))) + .map(|blob| Extractor::unique(blob, Default::default())) .reduce(Default::default, |mut a, b| { a.extend(b); a @@ -473,6 +470,7 @@ fn parse_all_blobs(blobs: Vec>) -> Vec { unsafe { String::from_utf8_unchecked(s.to_vec()) } }) .collect(); - result.sort(); + + result.par_sort(); result }