Typescript: Huge performance regression with tsc

Created on 30 Oct 2018  路  5Comments  路  Source: microsoft/TypeScript


TypeScript Version: 3.2.0-dev.20181030


Search Terms: slow performance

Code

I am speaking on behalf of AmCharts.

We have a large TypeScript library (which we sell to our customers). We tried to add in TypeScript 3.0 support to our library, but doing so caused massive slowdown issues. This is preventing our customers from using our library with TypeScript 3.0.

I have created two self-contained repos:

https://github.com/Pauan/amcharts4-typescript-example-fast
https://github.com/Pauan/amcharts4-typescript-example-slow

You can build them by using yarn install and then yarn build.

The fast repo is using [email protected], the slow repo is using typescript@next.

The fast repo builds in ~11 seconds. The slow repo builds in ~81 seconds!

Here is a diff between the two repos:

diff -r '--exclude=node_modules' '--exclude=.git' '--exclude=dist' amcharts4-typescript-example-fast/package.json amcharts4-typescript-example-slow/package.json
6c6
<     "typescript": "2.8.1",
---
>     "typescript": "next",
diff -r '--exclude=node_modules' '--exclude=.git' '--exclude=dist' amcharts4-typescript-example-fast/src/.internal/charts/types/TreeMap.ts amcharts4-typescript-example-slow/src/.internal/charts/types/TreeMap.ts
610c610
<               let xAxis = this.xAxes.push(new ValueAxis());
---
>               let xAxis = this.xAxes.push(new ValueAxis<this["_xAxisRendererType"]>());
625c625
<               let yAxis = this.yAxes.push(new ValueAxis());
---
>               let yAxis = this.yAxes.push(new ValueAxis<this["_yAxisRendererType"]>());
diff -r '--exclude=node_modules' '--exclude=.git' '--exclude=dist' amcharts4-typescript-example-fast/yarn.lock amcharts4-typescript-example-slow/yarn.lock
846,848c846,848
< [email protected]:
<   version "2.8.1"
<   resolved "http://registry.npmjs.org/typescript/-/typescript-2.8.1.tgz#6160e4f8f195d5ba81d4876f9c0cc1fbc0820624"
---
> typescript@next:
>   version "3.2.0-dev.20181030"
>   resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.0-dev.20181030.tgz#a80e3377101224aad5e1e3a341f320b5a9b9b1ef"

As you can see, aside from the tsc version change, only one very small code change was made (because it was required by typescript@next).

Expected behavior:

Compilation should be the same speed or faster.

Actual behavior:

Compilation is much slower.

Playground Link:
N/A

Related Issues:
https://github.com/Microsoft/TypeScript/issues/28025

Performance Needs Investigation

Most helpful comment

Looks like during comparison checking we don't appropriately share work when comparing type references instantiated with different this types. this["_dataItem"] becomes RadarColumnSeriesDataItem with a this of this["_dataItem"], which we then compare structurally against XYSeriesDataItem. We get a field _component of each, which on the this-instantiated source apparently becomes this["_dataItem"]["_component"] and of the second, a real type... we then against instantiate this and proceed like this to the fringes of the entire structure of types in the program, effectively instantiating every type on the source side of the relation with every path through which it could be reached (and checking that, yes, they are in fact still structurally assignable)! In effect, this types are disabling all cacheing.

All 5 comments

For content: In 2.8.1, we manufacture 61910 types (246955 symbols). In master, we manufacture 322305 types (1050759 symbols). That's 5.2x the types, and 4.2x the symbols; so we're certainly making more work for ourselves, given the same code. I'm still working out why, though

Something to do with how we're resolving the getX or getY calls in RadarColumnSeries, it seems like. Definitely triggered by the body of the validateDataElementReal method in some way (if you delete it, compile time goes all the way back down).

Yeah, the indexed this type in the parameter is causing us to perform an explosion of work when comparing signatures for some reason. If you make the signature

    public validateDataElementReal(dataItem: RadarColumnSeriesDataItem): void

instead of

    public validateDataElementReal(dataItem: this["_dataItem"]): void;

compile time also goes all the way back down. Still looking into _why_, but there's a workaround.

Looks like during comparison checking we don't appropriately share work when comparing type references instantiated with different this types. this["_dataItem"] becomes RadarColumnSeriesDataItem with a this of this["_dataItem"], which we then compare structurally against XYSeriesDataItem. We get a field _component of each, which on the this-instantiated source apparently becomes this["_dataItem"]["_component"] and of the second, a real type... we then against instantiate this and proceed like this to the fringes of the entire structure of types in the program, effectively instantiating every type on the source side of the relation with every path through which it could be reached (and checking that, yes, they are in fact still structurally assignable)! In effect, this types are disabling all cacheing.

I have confirmed that 3.2.0-dev.20181102 fixes the performance issue. Thanks a lot!

Was this page helpful?
0 / 5 - 0 ratings