Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exponential performance cliff compiling associated types with equality constraints #22204

Closed
nrc opened this issue Feb 12, 2015 · 21 comments
Closed
Assignees
Labels
A-associated-items Area: Associated items such as associated types and consts. C-enhancement Category: An issue proposing an enhancement or a PR with one. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-compiletime Issue: Problems and improvements with respect to compile times. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@nrc
Copy link
Member

nrc commented Feb 12, 2015

This was first observed due to a bunch of chain calls in rustdoc (#21694). @huonw provided a minimal test case:

struct Pair<A, B>(A, B);

trait Foo: Sized {
    type Item = ();
    fn foo<B>(self, other: B) -> Pair<Self, B> {
        Pair(self, other)
    }
}
impl Foo for () {
    type Item = ();
}
#[cfg(equality)]
impl<A: Foo, B: Foo<Item = A::Item>> Foo for Pair<A, B> {
    type Item = A::Item;
}
#[cfg(not(equality))]
impl<A: Foo, B: Foo> Foo for Pair<A, B> {
    type Item = A::Item;
}

fn main() {
    ().foo(())
        .foo(())
        .foo(())
        .foo(())
        .foo(())
        .foo(())
        .foo(())
        .foo(())
        .foo(())
        ;
}
@nrc nrc added I-slow Issue: Problems and improvements with respect to performance of generated code. A-associated-items Area: Associated items such as associated types and consts. labels Feb 12, 2015
@huonw
Copy link
Member

huonw commented Feb 12, 2015

This also applies for trait bounds, e.g. replacing the equality impl with

trait X<T> {}

impl X<()> for () {}

#[cfg(equality)]
impl<A: Foo, B: Foo> Foo for Pair<A, B>
        where A::Item: X<B::Item> {
    type Item = A::Item;
}

gives similar results.

@jdm
Copy link
Contributor

jdm commented Feb 12, 2015

cc me

@brson
Copy link
Contributor

brson commented Feb 12, 2015

Nominating because this seems to impact servo.

@pnkfelix
Copy link
Member

1.0 beta, P-high.

@pnkfelix pnkfelix added this to the 1.0 beta milestone Feb 12, 2015
@pnkfelix pnkfelix added P-medium Medium priority and removed I-nominated labels Feb 12, 2015
@nikomatsakis nikomatsakis self-assigned this Feb 12, 2015
@brson brson added the I-compiletime Issue: Problems and improvements with respect to compile times. label Feb 12, 2015
@larsbergstrom
Copy link
Contributor

I am not 100% sure we are hitting this - I believe that when we were investigating the perf regressions we suspected this, but I am not certain. Is there something we can do to verify we're hitting it? (assuming you would postpone it if it's not an issue for Servo).

@Gankra
Copy link
Contributor

Gankra commented Feb 13, 2015

#21694 demonstrates that if you're hitting it, it should show up in the type checking phase, which is otherwise quite lean. Should show up pretty clearly.

@gereeter
Copy link
Contributor

A slightly more minimal test case:

struct A<X>(X);

trait Foo: Sized {
    type Item;
    fn go(self) -> A<Self> {
        A(self)
    }
}

impl Foo for () {
    type Item = ();
}

#[cfg(equality)]
impl<X: Foo<Item=()>> Foo for A<X> {
    type Item = ();
}

#[cfg(not(equality))]
impl<X: Foo> Foo for A<X> {
    type Item = ();
}

fn main() {
    ().go().go().go().go()
      .go().go().go().go()
      .go().go().go().go()
      .go().go().go().go();
}

@emberian emberian removed the I-slow Issue: Problems and improvements with respect to performance of generated code. label Mar 25, 2015
@pnkfelix
Copy link
Member

1.0 polish (if anything; definitely puntable if we cannot fix in time).

@arielb1
Copy link
Contributor

arielb1 commented Jun 2, 2015

There are 2 problems here:
1) evaluate_predicates_recursively doesn't do memoization (this can easily be fixed).
2) we don't memoize associated types. this is harder to fix as they could have inferred and infer-skolemized contents, and generate nested obligations (esp. region obligations). I kind-of want to fix this already for trans, but trans doesn't have these difficulties.

@Ralle
Copy link

Ralle commented Oct 20, 2015

All of us using this crate ( https://github.com/Marwes/combine ) are waiting for this to get fixed. My compile time for a 500 line program is over 2 minutes. The maintainer of combine has helped people try to optimize their code for the compiler to run faster, not the result. I would very much like to see this ticket on any milestone, so I can look forward to Rust being usable in my parser project.

@nikomatsakis nikomatsakis added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Oct 21, 2015
@TheZalli
Copy link

Same as Ralle, I came here from combine. It seems like a really nice parsing library for my project, but even the simple demonstration test that was in their docs took alarmingly long to compile, so I hope this gets fixed soon.

@asajeffrey
Copy link

Ditto, coming here from perf issues with my parser library (#31849). My minimal example is just chaining iterators (https://play.rust-lang.org/?gist=a0c05fbf5c43234fb7c3):

fn main() {
    let iter = (0..2)
        .chain(0..2)
        .chain(0..2)
        .chain(0..2)
        .chain(0..2)
        .chain(0..2)
        .chain(0..2)
        .chain(0..2)
        .chain(0..2)
        .chain(0..2)
        .chain(0..2);
    for i in iter { println!("{:?}", i); }
}

On my machine, chaining 15 iterators takes 1min to typecheck, and uses about 500M of memory.

@asajeffrey
Copy link

Separating out the projection trait (HasItem) as a separate trait seems to be a work-around... https://play.rust-lang.org/?gist=1cf9886dd074e073cd6a

trait HasItem {
    type Item;
}

trait TDIterator<Item> {
    fn next(&mut self) -> Option<Item>;
}

trait BUIterator: HasItem + TDIterator<<Self as HasItem>::Item> {
}

@asajeffrey
Copy link

The work-around does scale up to the Parsell parser library (see, e.g. http://asajeffrey.github.io/parsell/target/doc/parsell/trait.StatefulInfer.html). It may be that combine can use a similar work-around? cc @Marwes.

@Marwes
Copy link
Contributor

Marwes commented Mar 1, 2016

@asajeffrey It would unfortunately be a backwards incompatible change so I am afraid I cannot do it at the moment for combine. I may do it for 2.0 though I am not inclinded to rush towards that at the moment.

(I did test compiling a branch I already had where combine's Input type is a parameter instead of associated and it cut compiling the tests into half 1m10s -> 32s)

@asajeffrey
Copy link

@Marwes indeed, Parsell has the luxury of still being at v0.x.x so I can make changes like this more easily.

@nikomatsakis
Copy link
Contributor

Just FYI, my projection caching branch (PR coming soon) totally solves this problem, from what I can tell.

@nikomatsakis
Copy link
Contributor

hmm, although actually maybe it's already been solved. At least stage0 seems to run fine too.

@nikomatsakis
Copy link
Contributor

I take it back, the chained iterator case is still quite slow.

@arielb1 arielb1 added the E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. label Jun 9, 2016
@arielb1
Copy link
Contributor

arielb1 commented Jun 9, 2016

This looks fixed with the projection cache.

@ishitatsuyuki
Copy link
Contributor

#48296 should have solved this completely. Closing as obsolete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-associated-items Area: Associated items such as associated types and consts. C-enhancement Category: An issue proposing an enhancement or a PR with one. E-needs-test Call for participation: An issue has been fixed and does not reproduce, but no test has been added. I-compiletime Issue: Problems and improvements with respect to compile times. P-medium Medium priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests