Skip to content

Commit cfd1e36

Browse files
authored
Significant performance improvements (#1308)
* Add performance hooks * Ignore v8 profile logs * Optimize TransformationContext 2% performance improvement * Remove WeakMap<TransformationContext, ...> 3-4% performance improvement * Change language extension declaration method * Significantly improve performance of language extensions >4x speedup!!! * Separate iterable language extensions * Unify language extension call transformers * Fix getExtensionsKindForType and error reporting for TableNew type * Cache function context * Fix crash on incorrect language extension use * Optimize and cleanup builtins 5% performance improvement * Optimize and cleanup annotations 3% performance improvement * Remove deprecated annotations 4-5% performance improvement * Optimize assignment-validation 5-6% performance improvement * Optimize language extensions again 2-3% performance improvement * Optimize getEmitPlan ~3% performance improvement in getEmitPlan (mostly IO) * Flatten visitor map * Ignore v8 cpu profiles * Fix prettier * PR feedback * Optimize isExplicitArrayType 6% transpilation time speedup typeToTypeNode is very slow * PR feedback * Revert changes to createReturnStatement * PR feedback
1 parent d69f089 commit cfd1e36

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+1374
-1810
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,7 @@ yarn.lock
99

1010
benchmark/data/*
1111
benchmark/dist/*
12+
13+
# v8 cpu profiles
14+
*-.log
15+
*.cpuprofile

language-extensions/index.d.ts

Lines changed: 72 additions & 74 deletions
Large diffs are not rendered by default.

src/CompilerOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export interface TypeScriptToLuaOptions {
3737
sourceMapTraceback?: boolean;
3838
tstlVerbose?: boolean;
3939
lua51AllowTryCatchInAsyncAwait?: boolean;
40+
measurePerformance?: boolean;
4041
}
4142

4243
export type CompilerOptions = OmitIndexSignature<ts.CompilerOptions> &

src/cli/parse.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ export const optionDeclarations: CommandLineOption[] = [
9999
description: "Always allow try/catch in async/await functions for Lua 5.1.",
100100
type: "boolean",
101101
},
102+
{
103+
name: "measurePerformance",
104+
description: "Measure performance of the tstl compiler.",
105+
type: "boolean",
106+
},
102107
];
103108

104109
export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): ParsedCommandLine {

src/lualib-build/plugin.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { SourceNode } from "source-map";
22
import * as ts from "typescript";
33
import * as tstl from "..";
44
import * as path from "path";
5-
import { getUsedLuaLibFeatures } from "../transformation/utils/lualib";
65
import { LuaLibFeature, LuaLibModulesInfo, luaLibModulesInfoFileName, resolveRecursiveLualibFeatures } from "../LuaLib";
76
import { EmitHost, ProcessedFile } from "../transpilation/utils";
87
import {
@@ -72,7 +71,7 @@ class LuaLibPlugin implements tstl.Plugin {
7271
// Transpile file as normal with tstl
7372
const fileResult = context.superTransformNode(file)[0] as tstl.File;
7473

75-
const usedFeatures = new Set<tstl.LuaLibFeature>(getUsedLuaLibFeatures(context));
74+
const usedFeatures = new Set<tstl.LuaLibFeature>(context.usedLuaLibFeatures);
7675

7776
// Get all imports in file
7877
const importNames = new Set<string>();

src/measure-performance.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { performance } from "perf_hooks";
2+
3+
// We use our own performance hooks implementation for easier use, but also call node's performance hooks, so it shows up in the profiler.
4+
5+
let enabled = false;
6+
const marks = new Map<string, number>();
7+
const durations = new Map<string, number>();
8+
9+
function timestamp() {
10+
return performance.now();
11+
}
12+
13+
/**
14+
* Marks a performance event, with the given markName.
15+
*/
16+
function mark(markName: string) {
17+
if (enabled) {
18+
marks.set(markName, timestamp());
19+
performance.mark(markName);
20+
}
21+
}
22+
23+
/**
24+
* Adds a performance measurement with the specified name.
25+
*
26+
* @param measureName The name of the performance measurement.
27+
* @param startMarkName The name of the starting mark
28+
* @param endMarkName The name of the ending mark
29+
*/
30+
function measure(measureName: string, startMarkName: string, endMarkName: string) {
31+
if (enabled) {
32+
const end = marks.get(endMarkName) ?? timestamp();
33+
const start = marks.get(startMarkName) ?? performance.timeOrigin;
34+
const previousDuration = durations.get(measureName) ?? 0;
35+
durations.set(measureName, previousDuration + (end - start));
36+
performance.measure(measureName, startMarkName, endMarkName);
37+
}
38+
}
39+
40+
/**
41+
* Starts a performance measurement section.
42+
* @param name name of the measurement
43+
*/
44+
export function startSection(name: string) {
45+
mark("start " + name);
46+
}
47+
48+
/**
49+
* Ends a performance measurement section.
50+
* @param name name of the measurement
51+
*/
52+
export function endSection(name: string) {
53+
mark("end " + name);
54+
measure(name, "start " + name, "end " + name);
55+
}
56+
57+
export function isMeasurementEnabled() {
58+
return enabled;
59+
}
60+
61+
export function enableMeasurement() {
62+
if (!enabled) {
63+
enabled = true;
64+
}
65+
}
66+
67+
export function disableMeasurement() {
68+
if (enabled) {
69+
enabled = false;
70+
marks.clear();
71+
durations.clear();
72+
}
73+
}
74+
75+
export function forEachMeasure(callback: (measureName: string, duration: number) => void) {
76+
durations.forEach((duration, measureName) => callback(measureName, duration));
77+
}
78+
79+
export function getTotalDuration() {
80+
let total = 0;
81+
forEachMeasure((_, duration) => (total += duration));
82+
return total;
83+
}

src/transformation/builtins/array.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { TransformationContext } from "../context";
44
import { unsupportedProperty } from "../utils/diagnostics";
55
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
66
import { transformArguments, transformCallAndArguments } from "../visitors/call";
7-
import { isStringType, isNumberType, findFirstNonOuterParent } from "../utils/typescript";
7+
import { findFirstNonOuterParent, typeAlwaysHasSomeOfFlags } from "../utils/typescript";
88
import { moveToPrecedingTemp } from "../visitors/expression-list";
99
import { isUnpackCall, wrapInTable } from "../utils/lua-ast";
1010

@@ -145,7 +145,10 @@ export function transformArrayPrototypeCall(
145145
case "join":
146146
const callerType = context.checker.getTypeAtLocation(calledMethod.expression);
147147
const elementType = context.checker.getElementTypeOfArrayType(callerType);
148-
if (elementType && (isStringType(context, elementType) || isNumberType(context, elementType))) {
148+
if (
149+
elementType &&
150+
typeAlwaysHasSomeOfFlags(context, elementType, ts.TypeFlags.StringLike | ts.TypeFlags.NumberLike)
151+
) {
149152
const defaultSeparatorLiteral = lua.createStringLiteral(",");
150153
const param = params[0];
151154
const parameters = [

src/transformation/builtins/global.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,27 @@ import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
55
import { isNumberType } from "../utils/typescript";
66
import { transformArguments } from "../visitors/call";
77

8-
export function transformGlobalCall(
8+
export function tryTransformBuiltinGlobalCall(
99
context: TransformationContext,
10-
node: ts.CallExpression
10+
node: ts.CallExpression,
11+
expressionType: ts.Type
1112
): lua.Expression | undefined {
12-
const signature = context.checker.getResolvedSignature(node);
13-
const parameters = transformArguments(context, node.arguments, signature);
14-
const expressionType = context.checker.getTypeAtLocation(node.expression);
13+
function getParameters() {
14+
const signature = context.checker.getResolvedSignature(node);
15+
return transformArguments(context, node.arguments, signature);
16+
}
17+
1518
const name = expressionType.symbol.name;
1619
switch (name) {
1720
case "SymbolConstructor":
18-
return transformLuaLibFunction(context, LuaLibFeature.Symbol, node, ...parameters);
21+
return transformLuaLibFunction(context, LuaLibFeature.Symbol, node, ...getParameters());
1922
case "NumberConstructor":
20-
return transformLuaLibFunction(context, LuaLibFeature.Number, node, ...parameters);
23+
return transformLuaLibFunction(context, LuaLibFeature.Number, node, ...getParameters());
2124
case "isNaN":
2225
case "isFinite":
2326
const numberParameters = isNumberType(context, expressionType)
24-
? parameters
25-
: [transformLuaLibFunction(context, LuaLibFeature.Number, undefined, ...parameters)];
27+
? getParameters()
28+
: [transformLuaLibFunction(context, LuaLibFeature.Number, undefined, ...getParameters())];
2629

2730
return transformLuaLibFunction(
2831
context,
@@ -31,8 +34,8 @@ export function transformGlobalCall(
3134
...numberParameters
3235
);
3336
case "parseFloat":
34-
return transformLuaLibFunction(context, LuaLibFeature.ParseFloat, node, ...parameters);
37+
return transformLuaLibFunction(context, LuaLibFeature.ParseFloat, node, ...getParameters());
3538
case "parseInt":
36-
return transformLuaLibFunction(context, LuaLibFeature.ParseInt, node, ...parameters);
39+
return transformLuaLibFunction(context, LuaLibFeature.ParseInt, node, ...getParameters());
3740
}
3841
}

0 commit comments

Comments
 (0)