Skip to content

Commit ed8ff7d

Browse files
authored
perf(codegen): unroll loop in SourcemapBuilder::update_generated_line_and_column (#13903)
Unrolled the loop in `SourcemapBuilder::update_generated_line_and_column`. This improved the perf on my machine by 3 - 8% (`just benchmark-one codegen`). I tried a closure with `#[inilne(always)]` but that didn't improve perf. So the code uses a macro instead.
1 parent 3ce0775 commit ed8ff7d

File tree

1 file changed

+56
-35
lines changed

1 file changed

+56
-35
lines changed

crates/oxc_codegen/src/sourcemap_builder.rs

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use std::path::Path;
22

33
use nonmax::NonMaxU32;
44

5-
use oxc_data_structures::slice_iter::SliceIter;
65
use oxc_index::{Idx, IndexVec};
76
use oxc_span::Span;
87
use oxc_syntax::identifier::{LS, PS};
@@ -247,56 +246,78 @@ impl<'a> SourcemapBuilder<'a> {
247246

248247
#[expect(clippy::cast_possible_truncation)]
249248
fn update_generated_line_and_column(&mut self, output: &[u8]) {
250-
let remaining = &output[self.last_generated_update..];
249+
const BATCH_SIZE: usize = 32;
250+
251+
let start_index = self.last_generated_update;
251252

252253
// Find last line break
253-
let mut line_start_ptr = remaining.as_ptr();
254+
let mut line_start_index = start_index;
255+
let mut idx = line_start_index;
254256
let mut last_line_is_ascii = true;
255-
let mut iter = remaining.iter();
256-
while let Some(&b) = iter.next() {
257-
match b {
258-
b'\n' => {}
259-
b'\r' => {
260-
// Handle Windows-specific "\r\n" newlines
261-
if iter.peek() == Some(&b'\n') {
262-
iter.next();
257+
258+
macro_rules! handle_byte {
259+
($byte:ident) => {
260+
match $byte {
261+
b'\n' => {}
262+
b'\r' => {
263+
// Handle Windows-specific "\r\n" newlines
264+
if output.get(idx + 1) == Some(&b'\n') {
265+
idx += 1;
266+
}
263267
}
264-
}
265-
_ if b.is_ascii() => {
266-
continue;
267-
}
268-
LS_OR_PS_FIRST_BYTE => {
269-
let next_byte = *iter.next().unwrap();
270-
let next_next_byte = *iter.next().unwrap();
271-
if !matches!([next_byte, next_next_byte], LS_LAST_2_BYTES | PS_LAST_2_BYTES) {
268+
_ if $byte.is_ascii() => {
269+
idx += 1;
270+
continue;
271+
}
272+
LS_OR_PS_FIRST_BYTE => {
273+
let next_byte = output[idx + 1];
274+
let next_next_byte = output[idx + 2];
275+
if !matches!([next_byte, next_next_byte], LS_LAST_2_BYTES | PS_LAST_2_BYTES)
276+
{
277+
last_line_is_ascii = false;
278+
idx += 1;
279+
continue;
280+
}
281+
}
282+
_ => {
283+
// Unicode char
272284
last_line_is_ascii = false;
285+
idx += 1;
273286
continue;
274287
}
275288
}
276-
_ => {
277-
// Unicode char
278-
last_line_is_ascii = false;
279-
continue;
280-
}
281-
}
282289

283-
// Line break found.
284-
// `iter` is now positioned after line break.
285-
line_start_ptr = iter.ptr();
286-
self.generated_line += 1;
287-
self.generated_column = 0;
288-
last_line_is_ascii = true;
290+
// Line break found.
291+
// `iter` is now positioned after line break.
292+
line_start_index = idx + 1;
293+
self.generated_line += 1;
294+
self.generated_column = 0;
295+
last_line_is_ascii = true;
296+
idx += 1;
297+
};
298+
}
299+
300+
while let (end, overflow) = idx.overflowing_add(BATCH_SIZE)
301+
&& !overflow
302+
&& end < output.len()
303+
{
304+
while idx < end {
305+
let b = output[idx];
306+
handle_byte!(b);
307+
}
308+
}
309+
while idx < output.len() {
310+
let b = output[idx];
311+
handle_byte!(b);
289312
}
290313

291314
// Calculate column
292315
self.generated_column += if last_line_is_ascii {
293-
// `iter` is now exhausted, so `iter.ptr()` is pointer to end of `output`
294-
(iter.ptr() as usize - line_start_ptr as usize) as u32
316+
(output.len() - line_start_index) as u32
295317
} else {
296-
let line_byte_offset = line_start_ptr as usize - remaining.as_ptr() as usize;
297318
// TODO: It'd be better if could use `from_utf8_unchecked` here, but we'd need to make this
298319
// function unsafe and caller guarantees `output` contains a valid UTF-8 string
299-
let last_line = std::str::from_utf8(&remaining[line_byte_offset..]).unwrap();
320+
let last_line = std::str::from_utf8(&output[line_start_index..]).unwrap();
300321
// Mozilla's "source-map" library counts columns using UTF-16 code units
301322
last_line.encode_utf16().count() as u32
302323
};

0 commit comments

Comments
 (0)