Add option to print size statistics of generated JS code

This commit is contained in:
Alexey Andreev 2018-10-03 11:08:15 +03:00
parent 614f95d376
commit 168bf1fa9c
4 changed files with 95 additions and 0 deletions

View File

@ -15,12 +15,18 @@
*/ */
package org.teavm.backend.javascript; package org.teavm.backend.javascript;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.Writer; import java.io.Writer;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -83,6 +89,9 @@ import org.teavm.vm.spi.RendererListener;
import org.teavm.vm.spi.TeaVMHostExtension; import org.teavm.vm.spi.TeaVMHostExtension;
public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
private static final NumberFormat STATS_NUM_FORMAT = new DecimalFormat("#,##0");
private static final NumberFormat STATS_PERCENT_FORMAT = new DecimalFormat("0.000 %");
private TeaVMTargetController controller; private TeaVMTargetController controller;
private boolean minifying = true; private boolean minifying = true;
private final Map<MethodReference, Generator> methodGenerators = new HashMap<>(); private final Map<MethodReference, Generator> methodGenerators = new HashMap<>();
@ -285,6 +294,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
for (RendererListener listener : rendererListeners) { for (RendererListener listener : rendererListeners) {
listener.begin(renderer, target); listener.begin(renderer, target);
} }
int start = sourceWriter.getOffset();
sourceWriter.append("\"use strict\";").newLine(); sourceWriter.append("\"use strict\";").newLine();
renderer.renderRuntime(); renderer.renderRuntime();
renderer.render(clsNodes); renderer.render(clsNodes);
@ -300,11 +310,46 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
for (RendererListener listener : rendererListeners) { for (RendererListener listener : rendererListeners) {
listener.complete(); listener.complete();
} }
int totalSize = sourceWriter.getOffset() - start;
printStats(renderer, totalSize);
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO Error occured", e); throw new RenderingException("IO Error occured", e);
} }
} }
private void printStats(Renderer renderer, int totalSize) {
if (!Boolean.parseBoolean(System.getProperty("teavm.js.stats", "false"))) {
return;
}
System.out.println("Total output size: " + STATS_NUM_FORMAT.format(totalSize));
System.out.println("Metadata size: " + getSizeWithPercentage(renderer.getMetadataSize(), totalSize));
System.out.println("String pool size: " + getSizeWithPercentage(renderer.getStringPoolSize(), totalSize));
ObjectIntMap<String> packageSizeMap = new ObjectIntHashMap<>();
for (String className : renderer.getClassesInStats()) {
String packageName = className.substring(0, className.lastIndexOf('.') + 1);
int classSize = renderer.getClassSize(className);
packageSizeMap.put(packageName, packageSizeMap.getOrDefault(packageName, 0) + classSize);
}
String[] packageNames = packageSizeMap.keys().toArray(String.class);
Arrays.sort(packageNames, Comparator.comparing(p -> -packageSizeMap.getOrDefault(p, 0)));
for (String packageName : packageNames) {
System.out.println("Package '" + packageName + "' size: "
+ getSizeWithPercentage(packageSizeMap.get(packageName), totalSize));
}
}
private String getSizeWithPercentage(int size, int totalSize) {
return STATS_NUM_FORMAT.format(size) + " (" + STATS_PERCENT_FORMAT.format((double) size / totalSize) + ")";
}
static class PackageNode {
String name;
Map<String, PackageNode> children = new HashMap<>();
}
private List<ClassNode> modelToAst(ListableClassHolderSource classes) { private List<ClassNode> modelToAst(ListableClassHolderSource classes) {
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(), AsyncMethodFinder asyncFinder = new AsyncMethodFinder(controller.getDependencyInfo().getCallGraph(),
controller.getDiagnostics()); controller.getDiagnostics());

View File

@ -19,4 +19,6 @@ public interface LocationProvider {
int getLine(); int getLine();
int getColumn(); int getColumn();
int getOffset();
} }

View File

@ -30,6 +30,7 @@ public class SourceWriter implements Appendable, LocationProvider {
private final int lineWidth; private final int lineWidth;
private int column; private int column;
private int line; private int line;
private int offset;
SourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) { SourceWriter(NamingStrategy naming, Appendable innerWriter, int lineWidth) {
this.naming = naming; this.naming = naming;
@ -62,6 +63,7 @@ public class SourceWriter implements Appendable, LocationProvider {
newLine(); newLine();
} else { } else {
column++; column++;
offset++;
} }
return this; return this;
} }
@ -92,6 +94,7 @@ public class SourceWriter implements Appendable, LocationProvider {
} }
appendIndent(); appendIndent();
column += end - start; column += end - start;
offset += end - start;
innerWriter.append(csq, start, end); innerWriter.append(csq, start, end);
} }
@ -149,6 +152,7 @@ public class SourceWriter implements Appendable, LocationProvider {
for (int i = 0; i < indentSize; ++i) { for (int i = 0; i < indentSize; ++i) {
innerWriter.append(" "); innerWriter.append(" ");
column += 4; column += 4;
offset += 4;
} }
lineStart = false; lineStart = false;
} }
@ -158,6 +162,7 @@ public class SourceWriter implements Appendable, LocationProvider {
innerWriter.append('\n'); innerWriter.append('\n');
column = 0; column = 0;
++line; ++line;
++offset;
lineStart = true; lineStart = true;
return this; return this;
} }
@ -169,6 +174,7 @@ public class SourceWriter implements Appendable, LocationProvider {
if (!minified) { if (!minified) {
innerWriter.append(' '); innerWriter.append(' ');
column++; column++;
offset++;
} }
} }
return this; return this;
@ -185,6 +191,7 @@ public class SourceWriter implements Appendable, LocationProvider {
if (!minified) { if (!minified) {
innerWriter.append('\n'); innerWriter.append('\n');
column = 0; column = 0;
++offset;
++line; ++line;
lineStart = true; lineStart = true;
} }
@ -214,4 +221,9 @@ public class SourceWriter implements Appendable, LocationProvider {
public int getLine() { public int getLine() {
return line; return line;
} }
@Override
public int getOffset() {
return offset;
}
} }

View File

@ -15,6 +15,8 @@
*/ */
package org.teavm.backend.javascript.rendering; package org.teavm.backend.javascript.rendering;
import com.carrotsearch.hppc.ObjectIntHashMap;
import com.carrotsearch.hppc.ObjectIntMap;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
@ -66,6 +68,10 @@ public class Renderer implements RenderingManager {
private RenderingContext context; private RenderingContext context;
private List<PostponedFieldInitializer> postponedFieldInitializers = new ArrayList<>(); private List<PostponedFieldInitializer> postponedFieldInitializers = new ArrayList<>();
private ObjectIntMap<String> sizeByClass = new ObjectIntHashMap<>();
private int stringPoolSize;
private int metadataSize;
public Renderer(SourceWriter writer, Set<MethodReference> asyncMethods, Set<MethodReference> asyncFamilyMethods, public Renderer(SourceWriter writer, Set<MethodReference> asyncMethods, Set<MethodReference> asyncFamilyMethods,
Diagnostics diagnostics, RenderingContext context) { Diagnostics diagnostics, RenderingContext context) {
this.naming = context.getNaming(); this.naming = context.getNaming();
@ -79,6 +85,22 @@ public class Renderer implements RenderingManager {
this.context = context; this.context = context;
} }
public int getStringPoolSize() {
return stringPoolSize;
}
public int getMetadataSize() {
return metadataSize;
}
public String[] getClassesInStats() {
return sizeByClass.keys().toArray(String.class);
}
public int getClassSize(String className) {
return sizeByClass.getOrDefault(className, 0);
}
@Override @Override
public SourceWriter getWriter() { public SourceWriter getWriter() {
return writer; return writer;
@ -133,6 +155,7 @@ public class Renderer implements RenderingManager {
return; return;
} }
try { try {
int start = writer.getOffset();
writer.append("$rt_stringPool(["); writer.append("$rt_stringPool([");
for (int i = 0; i < context.getStringPool().size(); ++i) { for (int i = 0; i < context.getStringPool().size(); ++i) {
if (i > 0) { if (i > 0) {
@ -141,6 +164,7 @@ public class Renderer implements RenderingManager {
writer.append('"').append(RenderingUtil.escapeString(context.getStringPool().get(i))).append('"'); writer.append('"').append(RenderingUtil.escapeString(context.getStringPool().get(i))).append('"');
} }
writer.append("]);").newLine(); writer.append("]);").newLine();
stringPoolSize = writer.getOffset() - start;
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO error", e); throw new RenderingException("IO error", e);
} }
@ -149,14 +173,21 @@ public class Renderer implements RenderingManager {
public void renderStringConstants() throws RenderingException { public void renderStringConstants() throws RenderingException {
try { try {
for (PostponedFieldInitializer initializer : postponedFieldInitializers) { for (PostponedFieldInitializer initializer : postponedFieldInitializers) {
int start = writer.getOffset();
writer.appendStaticField(initializer.field).ws().append("=").ws() writer.appendStaticField(initializer.field).ws().append("=").ws()
.append(context.constantToString(initializer.value)).append(";").softNewLine(); .append(context.constantToString(initializer.value)).append(";").softNewLine();
int sz = writer.getOffset() - start;
appendClassSize(initializer.field.getClassName(), sz);
} }
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO error", e); throw new RenderingException("IO error", e);
} }
} }
private void appendClassSize(String className, int sz) {
sizeByClass.put(className, sizeByClass.getOrDefault(className, 0) + sz);
}
public void renderRuntime() throws RenderingException { public void renderRuntime() throws RenderingException {
try { try {
renderSetCloneMethod(); renderSetCloneMethod();
@ -291,8 +322,10 @@ public class Renderer implements RenderingManager {
} }
} }
for (ClassNode cls : classes) { for (ClassNode cls : classes) {
int start = writer.getOffset();
renderDeclaration(cls); renderDeclaration(cls);
renderMethodBodies(cls); renderMethodBodies(cls);
appendClassSize(cls.getName(), writer.getOffset() - start);
} }
renderClassMetadata(classes); renderClassMetadata(classes);
} }
@ -452,6 +485,7 @@ public class Renderer implements RenderingManager {
} }
private void renderClassMetadata(List<ClassNode> classes) { private void renderClassMetadata(List<ClassNode> classes) {
int start = writer.getOffset();
try { try {
writer.append("$rt_metadata(["); writer.append("$rt_metadata([");
boolean first = true; boolean first = true;
@ -506,6 +540,8 @@ public class Renderer implements RenderingManager {
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO error occurred", e); throw new RenderingException("IO error occurred", e);
} }
metadataSize = writer.getOffset() - start;
} }
private void collectMethodsToCopyFromInterfaces(ClassReader cls, List<MethodReference> targetList) { private void collectMethodsToCopyFromInterfaces(ClassReader cls, List<MethodReference> targetList) {