Skip to content

Commit 409cd5a

Browse files
committed
performance improvements
1 parent d8c45e2 commit 409cd5a

7 files changed

Lines changed: 265 additions & 92 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/CompiletimeFunctionRunner.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ public class CompiletimeFunctionRunner {
5151
private final ImTranslator translator;
5252
private boolean injectObjects;
5353
private final Deque<Runnable> delayedActions = new ArrayDeque<>();
54+
private final Map<String, Long> compiletimeFunctionNanos = new LinkedHashMap<>();
55+
private long compiletimeExprNanos = 0L;
5456

5557
public ILInterpreter getInterpreter() {
5658
return interpreter;
@@ -97,21 +99,29 @@ public CompiletimeFunctionRunner(
9799

98100
public void run() {
99101
try {
102+
long t0 = System.nanoTime();
100103
List<Either<ImCompiletimeExpr, ImFunction>> toExecute = new ArrayList<>();
101104
collectCompiletimeExpressions(toExecute);
102105
collectCompiletimeFunctions(toExecute);
106+
long tCollected = System.nanoTime();
103107

104108
toExecute.sort(Comparator.comparing(this::getOrderIndex));
109+
long tSorted = System.nanoTime();
105110

106111
execute(toExecute);
112+
long tExecuted = System.nanoTime();
107113

108114

109115
if (functionFlag == FunctionFlagToRun.CompiletimeFunctions) {
110116
interpreter.writebackGlobalState(isInjectObjects());
111117
}
118+
long tWriteback = System.nanoTime();
112119
runDelayedActions();
120+
long tDelayed = System.nanoTime();
113121

114122
partitionCompiletimeStateInitFunction();
123+
long tPartitioned = System.nanoTime();
124+
logCompiletimeTiming(toExecute, t0, tCollected, tSorted, tExecuted, tWriteback, tDelayed, tPartitioned);
115125

116126
} catch (InterpreterException e) {
117127
Element origin = e.getTrace();
@@ -136,6 +146,48 @@ public void run() {
136146

137147
}
138148

149+
private void logCompiletimeTiming(List<Either<ImCompiletimeExpr, ImFunction>> toExecute,
150+
long t0, long tCollected, long tSorted, long tExecuted,
151+
long tWriteback, long tDelayed, long tPartitioned) {
152+
int exprCount = 0;
153+
int funcCount = 0;
154+
for (Either<ImCompiletimeExpr, ImFunction> e : toExecute) {
155+
if (e.isLeft()) {
156+
exprCount++;
157+
} else {
158+
funcCount++;
159+
}
160+
}
161+
WLogger.info(String.format(
162+
"Compiletime breakdown: total=%dms collect=%dms sort=%dms execute=%dms writeback=%dms delayed=%dms partition=%dms funcs=%d exprs=%d exprEval=%dms",
163+
ms(tPartitioned - t0),
164+
ms(tCollected - t0),
165+
ms(tSorted - tCollected),
166+
ms(tExecuted - tSorted),
167+
ms(tWriteback - tExecuted),
168+
ms(tDelayed - tWriteback),
169+
ms(tPartitioned - tDelayed),
170+
funcCount,
171+
exprCount,
172+
ms(compiletimeExprNanos)
173+
));
174+
if (!compiletimeFunctionNanos.isEmpty()) {
175+
List<Map.Entry<String, Long>> top = compiletimeFunctionNanos.entrySet().stream()
176+
.sorted((a, b) -> Long.compare(b.getValue(), a.getValue()))
177+
.limit(10)
178+
.collect(Collectors.toList());
179+
StringBuilder sb = new StringBuilder("Top compiletime functions:");
180+
for (Map.Entry<String, Long> e : top) {
181+
sb.append("\n ").append(e.getKey()).append(": ").append(ms(e.getValue())).append("ms");
182+
}
183+
WLogger.info(sb.toString());
184+
}
185+
}
186+
187+
private static long ms(long nanos) {
188+
return nanos / 1_000_000L;
189+
}
190+
139191
private void partitionCompiletimeStateInitFunction() {
140192
if (compiletimeStateInitFunction == null) {
141193
return;
@@ -220,6 +272,7 @@ public void visit(ImCompiletimeExpr e) {
220272

221273

222274
private void executeCompiletimeExpr(ImCompiletimeExpr cte) {
275+
long t0 = System.nanoTime();
223276
try {
224277
ProgramState globalState = interpreter.getGlobalState();
225278
globalState.setLastStatement(cte);
@@ -261,6 +314,8 @@ private void executeCompiletimeExpr(ImCompiletimeExpr cte) {
261314
e.setStacktrace(msg);
262315
e.setTrace(cte.attrTrace());
263316
throw e;
317+
} finally {
318+
compiletimeExprNanos += System.nanoTime() - t0;
264319
}
265320
}
266321

@@ -491,6 +546,7 @@ private ImFunction findNative(String funcName, WPos trace) {
491546

492547
private void executeCompiletimeFunction(ImFunction f) {
493548
if (functionFlag.matches(f)) {
549+
long t0 = System.nanoTime();
494550
try {
495551
if (!f.getBody().isEmpty()) {
496552
interpreter.getGlobalState().setLastStatement(f.getBody().get(0));
@@ -505,6 +561,8 @@ private void executeCompiletimeFunction(ImFunction f) {
505561
} catch (Throwable e) {
506562
failTests.put(f, Pair.create(interpreter.getLastStatement(), e.toString()));
507563
throw e;
564+
} finally {
565+
compiletimeFunctionNanos.merge(f.getName(), System.nanoTime() - t0, Long::sum);
508566
}
509567
}
510568
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameResolution.java

Lines changed: 74 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
import java.util.*;
1313

1414
public class NameResolution {
15+
private static final String PACKAGE_NAME_LOOKUP_PREFIX = "__pkg_name__";
16+
private static final String PACKAGE_TYPE_LOOKUP_PREFIX = "__pkg_type__";
17+
1518
private static String memberFuncCacheName(String name, WurstType receiverType) {
1619
return name
1720
+ "@"
@@ -20,6 +23,71 @@ private static String memberFuncCacheName(String name, WurstType receiverType) {
2023
+ System.identityHashCode(receiverType);
2124
}
2225

26+
private static ImmutableCollection<DefLink> scopeNameLinks(WScope scope, String name) {
27+
if (scope instanceof WPackage) {
28+
return packageNameLinks((WPackage) scope, name);
29+
}
30+
return scope.attrNameLinks().get(name);
31+
}
32+
33+
private static ImmutableCollection<TypeLink> scopeTypeLinks(WScope scope, String name) {
34+
if (scope instanceof WPackage) {
35+
return packageTypeLinks((WPackage) scope, name);
36+
}
37+
return scope.attrTypeNameLinks().get(name);
38+
}
39+
40+
private static ImmutableCollection<DefLink> packageNameLinks(WPackage p, String name) {
41+
GlobalCaches.CacheKey key = new GlobalCaches.CacheKey(p, PACKAGE_NAME_LOOKUP_PREFIX + name, GlobalCaches.LookupType.PACKAGE);
42+
@SuppressWarnings("unchecked")
43+
ImmutableCollection<DefLink> cached = (ImmutableCollection<DefLink>) GlobalCaches.lookupCache.get(key);
44+
if (cached != null) {
45+
return cached;
46+
}
47+
48+
LinkedHashSet<DefLink> result = new LinkedHashSet<>();
49+
boolean repl = p.getName().equals("WurstREPL");
50+
for (WImport imp : p.getImports()) {
51+
if (imp.getPackagename().equals("NoWurst")) {
52+
continue;
53+
}
54+
WPackage importedPackage = imp.attrImportedPackage();
55+
if (importedPackage == null) {
56+
continue;
57+
}
58+
if (repl) {
59+
result.addAll(importedPackage.getElements().attrNameLinks().get(name));
60+
result.addAll(importedPackage.attrNameLinks().get(name));
61+
} else {
62+
result.addAll(importedPackage.attrExportedNameLinks().get(name));
63+
}
64+
}
65+
ImmutableCollection<DefLink> links = ImmutableList.copyOf(result);
66+
GlobalCaches.lookupCache.put(key, links);
67+
return links;
68+
}
69+
70+
private static ImmutableCollection<TypeLink> packageTypeLinks(WPackage p, String name) {
71+
GlobalCaches.CacheKey key = new GlobalCaches.CacheKey(p, PACKAGE_TYPE_LOOKUP_PREFIX + name, GlobalCaches.LookupType.PACKAGE);
72+
@SuppressWarnings("unchecked")
73+
ImmutableCollection<TypeLink> cached = (ImmutableCollection<TypeLink>) GlobalCaches.lookupCache.get(key);
74+
if (cached != null) {
75+
return cached;
76+
}
77+
78+
LinkedHashSet<TypeLink> result = new LinkedHashSet<>();
79+
for (WImport imp : p.getImports()) {
80+
WPackage importedPackage = imp.attrImportedPackage();
81+
if (importedPackage == null) {
82+
continue;
83+
}
84+
result.addAll(importedPackage.attrExportedTypeNameLinks().get(name));
85+
}
86+
ImmutableCollection<TypeLink> links = ImmutableList.copyOf(result);
87+
GlobalCaches.lookupCache.put(key, links);
88+
return links;
89+
}
90+
2391
public static ImmutableCollection<FuncLink> lookupFuncsNoConfig(Element node, String name, boolean showErrors) {
2492
if (!showErrors) {
2593
GlobalCaches.CacheKey key = new GlobalCaches.CacheKey(node, name, GlobalCaches.LookupType.FUNC);
@@ -51,7 +119,7 @@ public static ImmutableCollection<FuncLink> lookupFuncsNoConfig(Element node, St
51119
Set<NameDef> seen = new HashSet<>();
52120

53121
for (WScope s : scopes) {
54-
Collection<DefLink> links = s.attrNameLinks().get(name);
122+
Collection<DefLink> links = scopeNameLinks(s, name);
55123
if (links.isEmpty()) continue;
56124

57125
for (DefLink n : links) {
@@ -170,7 +238,7 @@ public static ImmutableCollection<FuncLink> lookupMemberFuncs(Element node, Wurs
170238
}
171239

172240
for (WScope s : scopes) {
173-
Collection<DefLink> links = s.attrNameLinks().get(name);
241+
Collection<DefLink> links = scopeNameLinks(s, name);
174242
if (links.isEmpty()) continue;
175243

176244
for (DefLink n : links) {
@@ -245,7 +313,7 @@ public static NameLink lookupVarNoConfig(Element node, String name, boolean show
245313
}
246314
}
247315

248-
Collection<DefLink> links = s.attrNameLinks().get(name);
316+
Collection<DefLink> links = scopeNameLinks(s, name);
249317
if (links.isEmpty()) continue;
250318

251319
for (DefLink n : links) {
@@ -308,7 +376,7 @@ public static NameLink lookupMemberVar(Element node, WurstType receiverType, Str
308376
DefLinkMatch bestMatch = null;
309377

310378
for (WScope s : scopes) {
311-
Collection<DefLink> links = s.attrNameLinks().get(name);
379+
Collection<DefLink> links = scopeNameLinks(s, name);
312380
if (links.isEmpty()) continue;
313381

314382
DefLinkMatch candidate = findBestMemberVarMatch(links, receiverType, node, showErrors);
@@ -513,7 +581,7 @@ private static Iterable<TypeParamDef> typeParamsOfReceiverType(WurstType t) {
513581

514582

515583
for (WScope s : scopes) {
516-
ImmutableCollection<TypeLink> links = s.attrTypeNameLinks().get(name);
584+
ImmutableCollection<TypeLink> links = scopeTypeLinks(s, name);
517585
if (links.isEmpty()) continue;
518586

519587
for (NameLink n : links) {
@@ -556,7 +624,7 @@ private static Iterable<TypeParamDef> typeParamsOfReceiverType(WurstType t) {
556624
public static PackageLink lookupPackage(Element node, String name, boolean showErrors) {
557625
WScope scope = node.attrNearestScope();
558626
while (scope != null) {
559-
for (NameLink n : scope.attrNameLinks().get(name)) {
627+
for (NameLink n : scopeNameLinks(scope, name)) {
560628
if (n instanceof PackageLink) {
561629
return (PackageLink) n;
562630
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import static de.peeeq.wurstscript.translation.imoptimizer.UselessFunctionCallsRemover.isFunctionPure;
3030
import static de.peeeq.wurstscript.validation.GlobalCaches.LOCAL_STATE_CACHE;
31+
import static de.peeeq.wurstscript.validation.GlobalCaches.LOCAL_STATE_NOARG_CACHE;
3132

3233
public class ILInterpreter implements AbstractInterpreter {
3334
private ImProg prog;
@@ -294,12 +295,18 @@ private static LocalState runBuiltinFunction(ProgramState globalState, ImFunctio
294295
final String fname = f.getName();
295296
final boolean pure = isFunctionPure(fname);
296297

298+
if (pure && args.length == 0) {
299+
LocalState cachedNoArg = LOCAL_STATE_NOARG_CACHE.get(f);
300+
if (cachedNoArg != null) {
301+
return cachedNoArg;
302+
}
303+
}
304+
297305
GlobalCaches.ArgumentKey key = null;
306+
Object2ObjectOpenHashMap<GlobalCaches.ArgumentKey, LocalState> perFn = null;
298307
if (pure) {
299308
key = GlobalCaches.ArgumentKey.forLookup(args);
300-
301-
final Object2ObjectOpenHashMap<GlobalCaches.ArgumentKey, LocalState> perFn =
302-
LOCAL_STATE_CACHE.get(f);
309+
perFn = LOCAL_STATE_CACHE.get(f);
303310
if (perFn != null) {
304311
final LocalState cached = perFn.get(key);
305312
if (cached != null) {
@@ -317,9 +324,11 @@ private static LocalState runBuiltinFunction(ProgramState globalState, ImFunctio
317324
final LocalState localState = new LocalState(natives.invoke(fname, args));
318325

319326
if (pure) {
327+
if (args.length == 0) {
328+
LOCAL_STATE_NOARG_CACHE.put(f, localState);
329+
return localState;
330+
}
320331
// insert into per-function cache with bounded size
321-
Object2ObjectOpenHashMap<GlobalCaches.ArgumentKey, LocalState> perFn =
322-
LOCAL_STATE_CACHE.get(f);
323332
if (perFn == null) {
324333
perFn = new Object2ObjectOpenHashMap<>(16);
325334
LOCAL_STATE_CACHE.put(f, perFn);

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,50 @@ public JassProg translate() {
5858
* makes names unique in a consistent way
5959
*/
6060
private <T extends JassImElementWithName> void makeNamesUnique(List<T> list) {
61-
List<T> sorted = new ArrayList<>();
62-
for (T t : list) {
63-
sorted.add(t);
64-
}
61+
List<T> sorted = new ArrayList<>(list);
6562
sorted.sort(Comparator.comparing(JassImElementWithName::getName)
6663
.thenComparing(v -> v.getTrace().attrSource().getFile())
6764
.thenComparing(v -> v.getTrace().attrSource().getLine())
6865
.thenComparing(v -> v.getTrace().attrSource().getStartColumn()));
6966

70-
for (int i = 0; i < sorted.size(); i++) {
71-
T vi = sorted.get(i);
72-
for (int j = i + 1; j < sorted.size(); j++) {
73-
T vj = sorted.get(j);
74-
if (vi.getName().equals(vj.getName())) {
75-
vj.setName(vi.getName() + "_" + j);
67+
Set<String> used = new HashSet<>(sorted.size() * 2);
68+
Map<String, Integer> nextSuffix = new HashMap<>();
69+
70+
for (T v : sorted) {
71+
String base = v.getName();
72+
if (used.add(base)) {
73+
nextSuffix.putIfAbsent(base, 1);
74+
continue;
75+
}
76+
77+
int suffix = nextSuffix.getOrDefault(base, 1);
78+
String candidate;
79+
do {
80+
candidate = base + "_" + suffix;
81+
suffix++;
82+
} while (used.contains(candidate));
83+
84+
v.setName(candidate);
85+
used.add(candidate);
86+
nextSuffix.put(base, suffix);
87+
nextSuffix.putIfAbsent(candidate, 1);
88+
}
89+
}
90+
91+
private static final Pattern jassValidName = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]*");
92+
93+
private static de.peeeq.wurstscript.ast.Element getTrace(@Nullable Element elem) {
94+
while (elem != null) {
95+
if (elem instanceof ElementWithTrace) {
96+
ElementWithTrace ElementWithTrace = (ElementWithTrace) elem;
97+
de.peeeq.wurstscript.ast.Element t = ElementWithTrace.getTrace();
98+
if (t != null) {
99+
return t;
76100
}
77101
}
102+
elem = elem.getParent();
78103
}
104+
throw new Error("Could not get trace to original program.");
79105
}
80106

81107
private void collectGlobalVars() {
@@ -130,20 +156,6 @@ private List<ImFunction> sorted(Collection<ImFunction> collection) {
130156
return r;
131157
}
132158

133-
private static de.peeeq.wurstscript.ast.Element getTrace(@Nullable Element elem) {
134-
while (elem != null) {
135-
if (elem instanceof ElementWithTrace) {
136-
ElementWithTrace ElementWithTrace = (ElementWithTrace) elem;
137-
de.peeeq.wurstscript.ast.Element t = ElementWithTrace.getTrace();
138-
if (t != null) {
139-
return t;
140-
}
141-
}
142-
elem = elem.getParent();
143-
}
144-
throw new Error("Could not get trace to original program.");
145-
}
146-
147159
private void translateFunction(ImFunction imFunc) {
148160
if (imFunc.isBj() || imFunc.hasFlag(FunctionFlagEnum.IS_VARARG)) {
149161
return;
@@ -230,8 +242,6 @@ private String jassifyName(String name) {
230242
return name;
231243
}
232244

233-
private final Pattern jassValidName = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]*");
234-
235245
/**
236246
* replaces all invalid characters with underscores
237247
*/

0 commit comments

Comments
 (0)