Skip to content

Quadratic slowdown for lookup of bounds on associated types. #65510

Open
@eddyb

Description

@eddyb

There are two factors here, which we've spotted combined in the wild:

  1. we hoist all type X: ...; bounds to the trait-level, i.e.:
    trait Trait {
        type X: A + B;
        type Y: C + D;
    }
    is sugar for:
    trait Trait
    where
        Self::X: A,
        Self::X: B,
        Self::Y: C,
        Self::Y: D,
    {
        type X;
        type Y;
    }
    • I doubt we can do much about this, perhaps group/index the bounds?
  2. lookup of <_ as Foo>::X: Bar in Foo's where clauses appears to be quadratic
    • this wouldn't have a noticeable impact without 1.
    • probably easier to fix, I would've assumed it was already linear

And here's our stress test - apologies for the macro, but it takes a lot to push it into multiple seconds (the version in the wild had more sophisticated, and actually useful, macros):

macro_rules! stress {
    (type $name:ident: $($i:expr,)*) => {
        type $name: $(From<[(); $i]> + Into<[(); $i]> +)*;
    };
    (fn $($underscore:tt)*) => {
        const _: () = {
            $({fn _f<T: Trait>() {
                #[derive(Copy, Clone)]
                struct Foo<X>(X);
                let $underscore = Foo(T::X::default()).clone();
            }})*
        };
    };
}

trait Trait {
    type X: Copy + Default;

    // The bounds would normally be on many separate
    // associated types, but that makes no difference.
    stress!(type _Unused:
        00, 01, 02, 03, 04, 05, 06, 07, 08, 09,
        10, 11, 12, 13, 14, 15, 16, 17, 18, 19,

        // Uncomment to (almost) quadruple compile time:
        //20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
        //30, 31, 32, 33, 34, 35, 36, 37, 38, 39,

        // Add more to raise the total time to minutes.
    );
}

stress!(fn
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
);

Using -Zself-profile and summarize, we get this (times are total "self" times):

Query 20 (00-19) 40 (00-39) Slowdown
type_op_prove_predicate 2.52s 9.17s 3.64x
typeck_tables_of 1.78s 5.83s 3.28x
check_item_well_formed 1.43s 4.47s 3.13x

cc @rust-lang/wg-traits

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-associated-itemsArea: Associated items (types, constants & functions)A-trait-systemArea: Trait systemC-optimizationCategory: An issue highlighting optimization opportunities or PRs implementing suchI-compiletimeIssue: Problems and improvements with respect to compile times.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions