8059321: Decrease warmup time by caching common structures that were reused during parse

Reviewed-by: attila, shade
This commit is contained in:
Marcus Lagergren 2014-09-29 14:39:58 -07:00
parent 3d2c518ad8
commit 3d538d9cfd
7 changed files with 107 additions and 13 deletions

View File

@ -32,7 +32,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
import java.io.File;
import java.lang.invoke.MethodType;
import java.util.Arrays;
@ -153,6 +152,13 @@ public final class Compiler implements Loggable {
*/
private RecompilableScriptFunctionData compiledFunction;
/**
* Most compile unit names are longer than the default StringBuilder buffer,
* worth startup performance when massive class generation is going on to increase
* this
*/
private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32;
/**
* Compilation phases that a compilation goes through
*/
@ -631,7 +637,8 @@ public final class Compiler implements Loggable {
}
String nextCompileUnitName() {
final StringBuilder sb = new StringBuilder(firstCompileUnitName);
final StringBuilder sb = new StringBuilder(COMPILE_UNIT_NAME_BUFFER_SIZE);
sb.append(firstCompileUnitName);
final int cuid = nextCompileUnitId.getAndIncrement();
if (cuid > 0) {
sb.append("$cu").append(cuid);

View File

@ -590,8 +590,13 @@ public final class Label {
return label.getOffset() > other.label.getOffset();
}
private String str;
@Override
public String toString() {
return name + '_' + id;
if (str == null) {
str = name + '_' + id;
}
return str;
}
}

View File

@ -2576,12 +2576,55 @@ public class MethodEmitter implements Emitter {
*
* @param args debug information to print
*/
@SuppressWarnings("unused")
private void debug(final Object... args) {
if (debug) {
debug(30, args);
}
}
private void debug(final String arg) {
if (debug) {
debug(30, arg);
}
}
private void debug(final Object arg0, final Object arg1) {
if (debug) {
debug(30, new Object[] { arg0, arg1 });
}
}
private void debug(final Object arg0, final Object arg1, final Object arg2) {
if (debug) {
debug(30, new Object[] { arg0, arg1, arg2 });
}
}
private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3) {
if (debug) {
debug(30, new Object[] { arg0, arg1, arg2, arg3 });
}
}
private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4) {
if (debug) {
debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4 });
}
}
private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5) {
if (debug) {
debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5 });
}
}
private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5, final Object arg6) {
if (debug) {
debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6 });
}
}
/**
* Debug function that outputs generated bytecode and stack contents
* for a label - indentation is currently the only thing that differs

View File

@ -54,8 +54,10 @@ import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import jdk.internal.org.objectweb.asm.Handle;
@ -103,6 +105,16 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
/** The class for this type */
private final Class<?> clazz;
/**
* Cache for internal types - this is a query that requires complex stringbuilding inside
* ASM and it saves startup time to cache the type mappings
*/
private static final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> INTERNAL_TYPE_CACHE =
Collections.synchronizedMap(new WeakHashMap<Class<?>, jdk.internal.org.objectweb.asm.Type>());
/** Internal ASM type for this Type - computed once at construction */
private final jdk.internal.org.objectweb.asm.Type internalType;
/** Weights are used to decide which types are "wider" than other types */
protected static final int MIN_WEIGHT = -1;
@ -121,12 +133,13 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
* @param slots how many bytecode slots the type takes up
*/
Type(final String name, final Class<?> clazz, final int weight, final int slots) {
this.name = name;
this.clazz = clazz;
this.descriptor = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz);
this.weight = weight;
this.name = name;
this.clazz = clazz;
this.descriptor = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz);
this.weight = weight;
assert weight >= MIN_WEIGHT && weight <= MAX_WEIGHT : "illegal type weight: " + weight;
this.slots = slots;
this.slots = slots;
this.internalType = getInternalType(clazz);
}
/**
@ -356,11 +369,22 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
}
private jdk.internal.org.objectweb.asm.Type getInternalType() {
return jdk.internal.org.objectweb.asm.Type.getType(getTypeClass());
return internalType;
}
private static jdk.internal.org.objectweb.asm.Type lookupInternalType(final Class<?> type) {
final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> cache = INTERNAL_TYPE_CACHE;
jdk.internal.org.objectweb.asm.Type itype = cache.get(type);
if (itype != null) {
return itype;
}
itype = jdk.internal.org.objectweb.asm.Type.getType(type);
cache.put(type, itype);
return itype;
}
private static jdk.internal.org.objectweb.asm.Type getInternalType(final Class<?> type) {
return jdk.internal.org.objectweb.asm.Type.getType(type);
return lookupInternalType(type);
}
static void invokestatic(final MethodVisitor method, final Call call) {

View File

@ -30,7 +30,6 @@ import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT;
import static jdk.nashorn.internal.parser.TokenType.EOF;
import static jdk.nashorn.internal.parser.TokenType.EOL;
import static jdk.nashorn.internal.parser.TokenType.IDENT;
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.ir.IdentNode;

View File

@ -589,7 +589,9 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
}
MethodHandle lookupCodeMethod(final Class<?> codeClass, final MethodType targetType) {
log.info("Looking up ", DebugLogger.quote(name), " type=", targetType);
if (log.isEnabled()) {
log.info("Looking up ", DebugLogger.quote(name), " type=", targetType);
}
return MH.findStatic(LOOKUP, codeClass, functionName, targetType);
}

View File

@ -25,6 +25,9 @@
package jdk.nashorn.internal.runtime.regexp;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
import jdk.nashorn.internal.runtime.ParserException;
import jdk.nashorn.internal.runtime.options.Options;
@ -39,6 +42,15 @@ public class RegExpFactory {
private final static String JDK = "jdk";
private final static String JONI = "joni";
/** Weak cache of already validated regexps - when reparsing, we don't, for example
* need to recompile (reverify) all regexps that have previously been parsed by this
* RegExpFactory in a previous compilation. This saves significant time in e.g. avatar
* startup */
private static final Set<String> VALID_CACHE_SET =
Collections.newSetFromMap(
Collections.synchronizedMap(
new WeakHashMap<String, Boolean>()));
static {
final String impl = Options.getStringProperty("nashorn.regexp.impl", JONI);
switch (impl) {
@ -88,7 +100,9 @@ public class RegExpFactory {
*/
// @SuppressWarnings({"unused"})
public static void validate(final String pattern, final String flags) throws ParserException {
instance.compile(pattern, flags);
if (VALID_CACHE_SET.add(pattern + flags)) {
instance.compile(pattern, flags);
}
}
/**