Skip to content
This repository was archived by the owner on Apr 2, 2026. It is now read-only.

Commit 4c8c28c

Browse files
committed
Slightly faster jump table dispatch heuristic
1 parent cce2b2e commit 4c8c28c

3 files changed

Lines changed: 35 additions & 26 deletions

File tree

benches/parser.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@ fn bench_choice(c: &mut Criterion) {
3131
just('X'),
3232
just('Y'),
3333
just('Z'),
34-
))
35-
.repeated();
34+
));
3635

3736
let mut group = c.benchmark_group("choice");
3837

src/input.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1605,13 +1605,18 @@ impl<'src, 'parse, I: Input<'src>, E: ParserExtra<'src, I>> InputRef<'src, 'pars
16051605
self.next_ref_inner()
16061606
}
16071607

1608+
#[inline(always)]
1609+
pub(crate) fn peek_maybe_inner(&mut self) -> Option<I::MaybeToken> {
1610+
// SAFETY: cursor was generated by previous call to `Input::next`
1611+
unsafe { I::next_maybe(self.cache, &mut self.cursor.clone()) }
1612+
}
1613+
16081614
/// Peek the next token in the input. Returns `None` if the end of the input has been reached.
16091615
///
16101616
/// See [`InputRef::next_maybe`] for more information about what this function guarantees.
16111617
#[inline(always)]
16121618
pub fn peek_maybe(&mut self) -> Option<MaybeRef<'src, I::Token>> {
1613-
// SAFETY: cursor was generated by previous call to `Input::next`
1614-
unsafe { I::next_maybe(self.cache, &mut self.cursor.clone()).map(Into::into) }
1619+
self.peek_maybe_inner().map(Into::into)
16151620
}
16161621

16171622
/// Peek the next token in the input. Returns `None` if the end of the input has been reached.

src/primitive.rs

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -967,32 +967,37 @@ macro_rules! impl_choice_for_tuple {
967967
// Note that this tends to pessimise behaviour for EmptyErr (which presumably optimises to an jump table already),
968968
// so we detect this case and skip this optimisation using size_of.
969969
#[cfg(feature = "nightly")]
970-
if core::mem::size_of::<E::Error>() > 0 && $Head::Jump::SUPPORTS $(&& $X::Jump::SUPPORTS)* {
971-
if let Some(next) = inp.peek_maybe().as_deref() {
972-
match next {
973-
// The hope is that the optimiser folds this arm condition into the match in such a way that we end up with a jump table
974-
_ if $Head.will_match(&next) => return $Head.go::<M>(inp),
975-
$(
976-
_ if $X.will_match(&next) => return $X.go::<M>(inp),
977-
)*
978-
_ => return Err(()),
970+
if core::mem::size_of::<E::Error>() > 0
971+
&& $Head::Jump::SUPPORTS $(&& $X::Jump::SUPPORTS)*
972+
&& (1 $(+ $X::Jump::SUPPORTS as u64)*) > 1
973+
{
974+
return if let Some(next) = inp.peek_maybe_inner().as_ref().map(Borrow::borrow) {
975+
if $Head.will_match(&next) {
976+
$Head.go::<M>(inp)
977+
} $(else if $X.will_match(&next) {
978+
$X.go::<M>(inp)
979+
})* else {
980+
Err(())
979981
}
980-
}
981-
}
982-
983-
match $Head.go::<M>(inp) {
984-
Ok(out) => return Ok(out),
985-
Err(()) => inp.rewind(before.clone()),
986-
}
987-
988-
$(
989-
match $X.go::<M>(inp) {
982+
} else {
983+
Err(())
984+
};
985+
} else {
986+
match $Head.go::<M>(inp) {
990987
Ok(out) => return Ok(out),
991-
Err(()) => inp.rewind(before.clone()),
988+
Err(()) => {},
992989
}
993-
)*
994990

995-
Err(())
991+
$(
992+
inp.rewind(before.clone());
993+
match $X.go::<M>(inp) {
994+
Ok(out) => return Ok(out),
995+
Err(()) => {},
996+
}
997+
)*
998+
999+
Err(())
1000+
}
9961001
}
9971002

9981003
go_extra!(O);

0 commit comments

Comments
 (0)