Skip to content

Commit 3832a63

Browse files
committed
Optimize TokenStream::from_streams.
Currently, this function creates a new empty stream, and then appends the elements from each given stream onto that stream. This can cause quadratic behaviour. This commit changes the function so that it modifies the first stream (which can be long) by extending it with the elements from the subsequent streams (which are almost always short), which avoids the quadratic behaviour.
1 parent 421bd77 commit 3832a63

File tree

1 file changed

+37
-10
lines changed

1 file changed

+37
-10
lines changed

src/libsyntax/tokenstream.rs

+37-10
Original file line numberDiff line numberDiff line change
@@ -249,20 +249,47 @@ impl TokenStream {
249249
0 => TokenStream::empty(),
250250
1 => streams.pop().unwrap(),
251251
_ => {
252-
// rust-lang/rust#57735: pre-allocate vector to avoid
253-
// quadratic blow-up due to on-the-fly reallocations.
254-
let tree_count = streams.iter()
255-
.map(|ts| match &ts.0 { None => 0, Some(s) => s.len() })
252+
// We are going to extend the first stream in `streams` with
253+
// the elements from the subsequent streams. This requires
254+
// using `make_mut()` on the first stream, and in practice this
255+
// doesn't cause cloning 99.9% of the time.
256+
//
257+
// One very common use case is when `streams` has two elements,
258+
// where the first stream has any number of elements within
259+
// (often 1, but sometimes many more) and the second stream has
260+
// a single element within.
261+
262+
// Determine how much the first stream will be extended.
263+
// Needed to avoid quadratic blow up from on-the-fly
264+
// reallocations (#57735).
265+
let num_appends = streams.iter()
266+
.skip(1)
267+
.map(|ts| ts.len())
256268
.sum();
257-
let mut vec = Vec::with_capacity(tree_count);
258269

259-
for stream in streams {
260-
match stream.0 {
261-
None => {},
262-
Some(stream2) => vec.extend(stream2.iter().cloned()),
270+
// Get the first stream. If it's `None`, create an empty
271+
// stream.
272+
let mut iter = streams.drain();
273+
let mut first_stream_lrc = match iter.next().unwrap().0 {
274+
Some(first_stream_lrc) => first_stream_lrc,
275+
None => Lrc::new(vec![]),
276+
};
277+
278+
// Append the elements to the first stream, after reserving
279+
// space for them.
280+
let first_vec_mut = Lrc::make_mut(&mut first_stream_lrc);
281+
first_vec_mut.reserve(num_appends);
282+
for stream in iter {
283+
if let Some(stream) = stream.0 {
284+
first_vec_mut.extend(stream.iter().cloned());
263285
}
264286
}
265-
TokenStream::new(vec)
287+
288+
// Create the final `TokenStream`.
289+
match first_vec_mut.len() {
290+
0 => TokenStream(None),
291+
_ => TokenStream(Some(first_stream_lrc)),
292+
}
266293
}
267294
}
268295
}

0 commit comments

Comments
 (0)