Skip to content

Commit fcf2b24

Browse files
committed
Introduce MatcherPosHandle.
This lets us store most `MatcherPos` instances on the stack. This speeds up various runs of html5ever, the best by 3%.
1 parent 6872377 commit fcf2b24

File tree

1 file changed

+59
-11
lines changed

1 file changed

+59
-11
lines changed

src/libsyntax/ext/tt/macro_parser.rs

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ use tokenstream::TokenStream;
9797
use util::small_vector::SmallVector;
9898

9999
use std::mem;
100+
use std::ops::{Deref, DerefMut};
100101
use std::rc::Rc;
101102
use std::collections::HashMap;
102103
use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -186,7 +187,7 @@ struct MatcherPos<'a> {
186187
sep: Option<Token>,
187188
/// The "parent" matcher position if we are in a repetition. That is, the matcher position just
188189
/// before we enter the sequence.
189-
up: Option<Box<MatcherPos<'a>>>,
190+
up: Option<MatcherPosHandle<'a>>,
190191

191192
// Specifically used to "unzip" token trees. By "unzip", we mean to unwrap the delimiters from
192193
// a delimited token tree (e.g. something wrapped in `(` `)`) or to get the contents of a doc
@@ -206,6 +207,49 @@ impl<'a> MatcherPos<'a> {
206207
}
207208
}
208209

210+
// Lots of MatcherPos instances are created at runtime. Allocating them on the
211+
// heap is slow. Furthermore, using SmallVec<MatcherPos> to allocate them all
212+
// on the stack is also slow, because MatcherPos is quite a large type and
213+
// instances get moved around a lot between vectors, which requires lots of
214+
// slow memcpy calls.
215+
//
216+
// Therefore, the initial MatcherPos is always allocated on the stack,
217+
// subsequent ones (of which there aren't that many) are allocated on the heap,
218+
// and this type is used to encapsulate both cases.
219+
enum MatcherPosHandle<'a> {
220+
Ref(&'a mut MatcherPos<'a>),
221+
Box(Box<MatcherPos<'a>>),
222+
}
223+
224+
impl<'a> Clone for MatcherPosHandle<'a> {
225+
// This always produces a new Box.
226+
fn clone(&self) -> Self {
227+
MatcherPosHandle::Box(match *self {
228+
MatcherPosHandle::Ref(ref r) => Box::new((**r).clone()),
229+
MatcherPosHandle::Box(ref b) => b.clone(),
230+
})
231+
}
232+
}
233+
234+
impl<'a> Deref for MatcherPosHandle<'a> {
235+
type Target = MatcherPos<'a>;
236+
fn deref(&self) -> &Self::Target {
237+
match *self {
238+
MatcherPosHandle::Ref(ref r) => r,
239+
MatcherPosHandle::Box(ref b) => b,
240+
}
241+
}
242+
}
243+
244+
impl<'a> DerefMut for MatcherPosHandle<'a> {
245+
fn deref_mut(&mut self) -> &mut MatcherPos<'a> {
246+
match *self {
247+
MatcherPosHandle::Ref(ref mut r) => r,
248+
MatcherPosHandle::Box(ref mut b) => b,
249+
}
250+
}
251+
}
252+
209253
/// Represents the possible results of an attempted parse.
210254
pub enum ParseResult<T> {
211255
/// Parsed successfully.
@@ -241,10 +285,10 @@ fn create_matches(len: usize) -> Vec<Rc<Vec<NamedMatch>>> {
241285

242286
/// Generate the top-level matcher position in which the "dot" is before the first token of the
243287
/// matcher `ms` and we are going to start matching at position `lo` in the source.
244-
fn initial_matcher_pos(ms: &[TokenTree], lo: BytePos) -> Box<MatcherPos> {
288+
fn initial_matcher_pos(ms: &[TokenTree], lo: BytePos) -> MatcherPos {
245289
let match_idx_hi = count_names(ms);
246290
let matches = create_matches(match_idx_hi);
247-
Box::new(MatcherPos {
291+
MatcherPos {
248292
// Start with the top level matcher given to us
249293
top_elts: TtSeq(ms), // "elts" is an abbr. for "elements"
250294
// The "dot" is before the first token of the matcher
@@ -267,7 +311,7 @@ fn initial_matcher_pos(ms: &[TokenTree], lo: BytePos) -> Box<MatcherPos> {
267311
seq_op: None,
268312
sep: None,
269313
up: None,
270-
})
314+
}
271315
}
272316

273317
/// `NamedMatch` is a pattern-match result for a single `token::MATCH_NONTERMINAL`:
@@ -396,10 +440,10 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool {
396440
/// A `ParseResult`. Note that matches are kept track of through the items generated.
397441
fn inner_parse_loop<'a>(
398442
sess: &ParseSess,
399-
cur_items: &mut SmallVector<Box<MatcherPos<'a>>>,
400-
next_items: &mut Vec<Box<MatcherPos<'a>>>,
401-
eof_items: &mut SmallVector<Box<MatcherPos<'a>>>,
402-
bb_items: &mut SmallVector<Box<MatcherPos<'a>>>,
443+
cur_items: &mut SmallVector<MatcherPosHandle<'a>>,
444+
next_items: &mut Vec<MatcherPosHandle<'a>>,
445+
eof_items: &mut SmallVector<MatcherPosHandle<'a>>,
446+
bb_items: &mut SmallVector<MatcherPosHandle<'a>>,
403447
token: &Token,
404448
span: syntax_pos::Span,
405449
) -> ParseResult<()> {
@@ -502,7 +546,7 @@ fn inner_parse_loop<'a>(
502546
}
503547

504548
let matches = create_matches(item.matches.len());
505-
cur_items.push(Box::new(MatcherPos {
549+
cur_items.push(MatcherPosHandle::Box(Box::new(MatcherPos {
506550
stack: vec![],
507551
sep: seq.separator.clone(),
508552
seq_op: Some(seq.op),
@@ -514,7 +558,7 @@ fn inner_parse_loop<'a>(
514558
up: Some(item),
515559
sp_lo: sp.lo(),
516560
top_elts: Tt(TokenTree::Sequence(sp, seq)),
517-
}));
561+
})));
518562
}
519563

520564
// We need to match a metavar (but the identifier is invalid)... this is an error
@@ -596,7 +640,11 @@ pub fn parse(
596640
// processes all of these possible matcher positions and produces posible next positions into
597641
// `next_items`. After some post-processing, the contents of `next_items` replenish `cur_items`
598642
// and we start over again.
599-
let mut cur_items = SmallVector::one(initial_matcher_pos(ms, parser.span.lo()));
643+
//
644+
// This MatcherPos instance is allocated on the stack. All others -- and
645+
// there are frequently *no* others! -- are allocated on the heap.
646+
let mut initial = initial_matcher_pos(ms, parser.span.lo());
647+
let mut cur_items = SmallVector::one(MatcherPosHandle::Ref(&mut initial));
600648
let mut next_items = Vec::new();
601649

602650
loop {

0 commit comments

Comments
 (0)