-
Notifications
You must be signed in to change notification settings - Fork 13.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #78317 - est31:linear_in_impl_count, r=matthewjasper
Turn quadratic time on number of impl blocks into linear time Previously, if you had a lot of inherent impl blocks on a type like: ```Rust struct Foo; impl Foo { fn foo_1() {} } // ... impl Foo { fn foo_100_000() {} } ``` The compiler would be very slow at processing it, because an internal algorithm would run in O(n^2), where n is the number of impl blocks. Now, we add a new algorithm that allocates but is faster asymptotically. Comparing rustc nightly with a local build of rustc as of this PR (results in seconds): | N | real time before | real time after | | - | - | - | | 4_000 | 0.57 | 0.46 | | 8_000 | 1.31 | 0.84 | | 16_000 | 3.56 | 1.69 | | 32_000 | 10.60 | 3.73 | I've tuned up the numbers to make the effect larger than the startup noise of rustc, but the asymptotic difference should hold for smaller n as well. Note: current state of the PR omits error messages if there are other errors present already. For now, I'm mainly interested in a perf run to study whether this issue is present at all. Please queue one for this PR. Thanks!
- Loading branch information
Showing
5 changed files
with
365 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
54 changes: 54 additions & 0 deletions
54
src/test/ui/inherent-impls-overlap-check/auxiliary/repeat.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// force-host | ||
// no-prefer-dynamic | ||
|
||
#![crate_type = "proc-macro"] | ||
|
||
extern crate proc_macro; | ||
use proc_macro::{Ident, Group, TokenStream, TokenTree as Tt}; | ||
|
||
// This constant has to be above the ALLOCATING_ALGO_THRESHOLD | ||
// constant in inherent_impls_overlap.rs | ||
const REPEAT_COUNT: u32 = 501; | ||
|
||
#[proc_macro] | ||
/// Repeats the input many times, while replacing idents | ||
/// named "IDENT" with "id_$v", where v is a counter. | ||
pub fn repeat_with_idents(input: TokenStream) -> TokenStream { | ||
let mut res = Vec::new(); | ||
fn visit_stream(res: &mut Vec<Tt>, stream :TokenStream, v: u32) { | ||
let mut stream_iter = stream.into_iter(); | ||
while let Some(tt) = stream_iter.next() { | ||
match tt { | ||
Tt::Group(group) => { | ||
let tt = Tt::Group(visit_group(group, v)); | ||
res.push(tt); | ||
}, | ||
Tt::Ident(id) => { | ||
let id = if &id.to_string() == "IDENT" { | ||
Ident::new(&format!("id_{}", v), id.span()) | ||
} else { | ||
id | ||
}; | ||
res.push(Tt::Ident(id)); | ||
}, | ||
Tt::Punct(p) => { | ||
res.push(Tt::Punct(p)); | ||
}, | ||
Tt::Literal(lit) => { | ||
res.push(Tt::Literal(lit)); | ||
}, | ||
} | ||
} | ||
} | ||
fn visit_group(group :Group, v: u32) -> Group { | ||
let mut res = Vec::new(); | ||
visit_stream(&mut res, group.stream(), v); | ||
let stream = res.into_iter().collect(); | ||
let delim = group.delimiter(); | ||
Group::new(delim, stream) | ||
} | ||
for v in 0 .. REPEAT_COUNT { | ||
visit_stream(&mut res, input.clone(), v) | ||
} | ||
res.into_iter().collect() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
// run-pass | ||
// aux-build:repeat.rs | ||
|
||
// This tests the allocating algo branch of the | ||
// inherent impls overlap checker. | ||
// This branch was added by PR: | ||
// https://github.com/rust-lang/rust/pull/78317 | ||
// In this test, we repeat many impl blocks | ||
// to trigger the allocating branch. | ||
|
||
#![allow(unused)] | ||
|
||
extern crate repeat; | ||
|
||
// Simple case where each impl block is distinct | ||
|
||
struct Foo {} | ||
|
||
repeat::repeat_with_idents!(impl Foo { fn IDENT() {} }); | ||
|
||
// There are overlapping impl blocks but due to generics, | ||
// they may overlap. | ||
|
||
struct Bar<T>(T); | ||
|
||
struct A; | ||
struct B; | ||
|
||
repeat::repeat_with_idents!(impl Bar<A> { fn IDENT() {} }); | ||
|
||
impl Bar<A> { fn foo() {} } | ||
impl Bar<B> { fn foo() {} } | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// aux-build:repeat.rs | ||
|
||
#![allow(unused)] | ||
|
||
// This tests the allocating algo branch of the | ||
// inherent impls overlap checker. | ||
// This branch was added by PR: | ||
// https://github.com/rust-lang/rust/pull/78317 | ||
// In this test, we repeat many impl blocks | ||
// to trigger the allocating branch. | ||
|
||
// Simple overlap | ||
|
||
extern crate repeat; | ||
|
||
struct Foo {} | ||
|
||
repeat::repeat_with_idents!(impl Foo { fn IDENT() {} }); | ||
|
||
impl Foo { fn hello() {} } //~ERROR duplicate definitions with name `hello` | ||
impl Foo { fn hello() {} } | ||
|
||
// Transitive overlap | ||
|
||
struct Foo2 {} | ||
|
||
repeat::repeat_with_idents!(impl Foo2 { fn IDENT() {} }); | ||
|
||
impl Foo2 { | ||
fn bar() {} | ||
fn hello2() {} //~ERROR duplicate definitions with name `hello2` | ||
} | ||
|
||
impl Foo2 { | ||
fn baz() {} | ||
fn hello2() {} | ||
} | ||
|
||
// Slightly stronger transitive overlap | ||
|
||
struct Foo3 {} | ||
|
||
repeat::repeat_with_idents!(impl Foo3 { fn IDENT() {} }); | ||
|
||
impl Foo3 { | ||
fn bar() {} //~ERROR duplicate definitions with name `bar` | ||
fn hello3() {} //~ERROR duplicate definitions with name `hello3` | ||
} | ||
|
||
impl Foo3 { | ||
fn bar() {} | ||
fn hello3() {} | ||
} | ||
|
||
// Generic overlap | ||
|
||
struct Bar<T>(T); | ||
|
||
struct A; | ||
struct B; | ||
|
||
repeat::repeat_with_idents!(impl Bar<A> { fn IDENT() {} }); | ||
|
||
impl Bar<A> { fn foo() {} fn bar2() {} } | ||
impl Bar<B> { | ||
fn foo() {} | ||
fn bar2() {} //~ERROR duplicate definitions with name `bar2` | ||
} | ||
impl Bar<B> { fn bar2() {} } | ||
|
||
fn main() {} |
Oops, something went wrong.