Skip to content

Performance regression from #48044 #52345

Description

@epmatsw

Bug Report

It seems like a fairly significant performance regression was introduced in this #48044, which was shipped in TS 4.7. In TS 4.6, it takes ~30 seconds to run yarn tsc in our repo, and the reported strict subtype cache size is 24_011. In TS 4.7, it takes 75+ seconds to run yarn tsc (~2.5x longer than previous), and the reported strict subtype cache size is 122_778 (~5x larger than previous).

I verified that it was that specific PR that regressed the performance by taking a locally-installed 4.6.4 and applying the changes from that PR directly.

Another symptom is that several types (mostly spreads of objects into others) are now reporting as "Expression produces a union type that is too complex to represent."

🔎 Search Terms

Expression produces a union type that is too complex to represent, strict subtype cache size

🕗 Version & Regression Information

Version Time Strict Subtype Cache Size
4.6.4 33s 24011
4.7.3 75s 122778
4.9.4 77s 122869
5.0.0-dev.20230120 78s 122869

💻 Code

I haven't been able to narrow down a minimal reproduction of this issue, but if someone points me in the right direction I'm happy to work on it.

🙁 Actual behavior

Running yarn tsc --noEmit --extendedDiagnostics takes ~75 seconds in our repo, with a Strict subtype cache size of 122778.

🙂 Expected behavior

Running yarn tsc --noEmit --extendedDiagnostics takes ~30 seconds in our repo, with a Strict subtype cache size of 24011.

Activity

  1. DanielRosenwasser commented on Jan 21, 2023

    @DanielRosenwasser
    Member

    Would you be able to generate a trace and use our analyze-trace tool?

    https://github.com/microsoft/typescript-analyze-trace

    It should highlight specific hot spots that might occur as of TS 4.7. The usage instructions are a little better now. Let me know if you need any more info using it.

  2. jakebailey commented on Jan 26, 2023

    @jakebailey
    Member

    Will Stamper (@epmatsw) In lieu of a small repro, is your code public such that I can test it out?

    The above tracing mechanism should get you some info about which types are problematic, but to work on this I do need something, so even the full thing would be helpful.

  3. epmatsw commented on Jan 27, 2023

    @epmatsw
    Author

    Not public, unfortunately. Sorry about the delay on the trace, I’ll try to get something going tomorrow.

  4. epmatsw commented on Jan 27, 2023

    @epmatsw
    Author

    Dang, thought I had a reproduction but apparently not. Probably not gonna get something until Monday now :(

  5. epmatsw commented on Jan 28, 2023

    @epmatsw
    Author

    Okay, so I'm not sure this is minimal, but I think it's on the right track:

    type R = `${number}a` & {
        _thing: true;
    };
    
    type _S = "1" | "2" | "3" | "4" | "5";
    type S = `${_S}${_S}`;
    
    // Produces "Expression produces a union type that is too complex to represent"
    // type S = `${_S}${_S}${_S}`;
    
    
    type T = R | S;
    type X = `${T} ${T}`;
    
    export type Props = Partial<{
        x: X;
    }>;
    
    const a1: Props = {};
    const a2: Props = {};
    
    const b = { ...a1, ...a2 };
    
    export { b };

    In 4.6.4, that has a Strict subtype cache size of 0. In 4.7.4, that has a Strict subtype cache size of 34476.

  6. epmatsw commented on Jan 28, 2023

    @epmatsw
    Author

    Hmmm. I guess because X boils down to string in 4.6.4. So I'm not sure how useful that even actually is other than "actually checking this gigantic type rather than not checking it is expensive" :/

  7. jakebailey commented on Jan 28, 2023

    @jakebailey
    Member

    That might be enough, actually. By modifiying it a bit to try and make it worse (but not enough to make it go to string), I can get the test to take a good 5-6 seconds on my machine, wheras in v4.6, it takes under a second.

    type R = `${number}a` & {
        _thing: true;
    };
    
    type _S = "1" | "2" | "3" | "4" | "5" | "6";
    
    type S = `${_S}${_S}${_S}`;
    
    
    type T = R | S;
    type X = `${T} ${T}`;
    
    export type Props = Partial<{
        x: X;
    }>;
    
    const a1: Props = {};
    const a2: Props = {};
    
    const b = { ...a1, ...a2 };
    
    export { b };
  8. jakebailey commented on Feb 16, 2023

    @jakebailey
    Member

    After staring at this for a while, I'm at a loss for a way to speed this up; we're now actually traversing the intersections whereas before, we weren't. This example in particular turns into a combinatorial explosion after #48044, which I didn't really see coming at the time.

    I'm not sure what the path forward is at the moment, besides reverting that PR (or doing nothing), but I'll keep trying.

  9. jakebailey commented on Feb 18, 2023

    @jakebailey
    Member

    Will Stamper (@epmatsw) Would you mind testing the package on my PR for this issue here? #52836 (comment)

    I believe it fixes the performance problem (in that it avoids massive intersections), but, it does make these sorts of constructs stricter (more correct in some situations) and the above sample in particular will still give an error that complains about it being too big.

    EDIT: updated the link to the latest version

  10. epmatsw commented on Feb 21, 2023

    @epmatsw
    Author

    Unfortunately not a huge improvement:

    TS 4.6.4

    Strict subtype cache size:    24040
    Total time:                  34.40s
    

    TS Version 5.0.0-insiders.20230218 (https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/146938/artifacts?artifactName=tgz&fileId=E6600AFB90EEF24611686B292C01E0D427A0C8AA2B90E3E4B7E85880B3CF614102&fileName=/typescript-5.0.0-insiders.20230218.tgz):

    Strict subtype cache size:   122673
    Total time:                  65.03s
    
  11. jakebailey commented on Feb 21, 2023

    @jakebailey
    Member

    Just to confirm, it's ~13s faster than a previous 5.0 build, correct?

  12. 29 remaining items

  13. epmatsw commented on Sep 30, 2023

    @epmatsw
    Author
  14. jakebailey commented on Sep 30, 2023

    @jakebailey
  15. epmatsw commented on Sep 30, 2023

    @epmatsw
    Author
  16. epmatsw commented on Sep 30, 2023

    @epmatsw
    Author
  17. jakebailey commented on Oct 1, 2023

    @jakebailey
  18. epmatsw commented on Oct 2, 2023

    @epmatsw
    Author
  19. jakebailey commented on Oct 12, 2023

    @jakebailey
    Member

    So barring the stuff in #55948, is there anything left here that isn't fast? I've gone through the code bits in this thread and they have all improved, and I can't really recall what's left here. It'd be nice of course to actually be able to test the real world example... I am of course happy to test it and if privacy is a concern I'm pretty sure we do NDAs.

  20. epmatsw commented on Oct 12, 2023

    @epmatsw
    Author

    I think it's safe to call this one fixed! The remaining regression since 4.6 is fully accounted for by the issue we're tracking in the other thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Domain: PerformanceReports of unusually slow behaviorNeeds InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions