# HG changeset patch # User Doug Simon # Date 1417039863 -3600 # Node ID 84bef219afc7ab09d4c7bb2b4210b9e0ba382ef2 # Parent 1019a5d7c06591639e8808d6bdc87ff0c278ff7a speed up replay compilation testing by re-using object pool across replay contexts diff -r 1019a5d7c065 -r 84bef219afc7 graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Context.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Context.java Wed Nov 26 23:05:24 2014 +0100 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Context.java Wed Nov 26 23:11:03 2014 +0100 @@ -41,6 +41,7 @@ public class Context implements AutoCloseable { private static final ThreadLocal currentContext = new ThreadLocal<>(); + private static final ThreadLocal previousContext = new ThreadLocal<>(); private final Map, Fields> fieldsMap = new HashMap<>(); @@ -328,13 +329,15 @@ return srcValue; } dstValue = pool.get(srcValue); + if (dstValue == null && previousPool != null) { + dstValue = previousPool.get(srcValue); + } if (dstValue == null) { if (srcValue instanceof Remote) { dstValue = get(srcValue); } else { dstValue = copies.get(srcValue); if (dstValue == null) { - assert !worklist.contains(srcValue) : id(srcValue); // System.out.printf("worklist+: %s%n", s(srcValue)); worklist.add(srcValue); copies.put(srcValue, copies); @@ -356,6 +359,7 @@ * In addition, copies in {@link #pool} are re-used. */ private Object copy(Object root) { + long start = System.currentTimeMillis(); assert !(isAssignableTo(root.getClass(), DontCopyClasses) || SharedGlobals.containsKey(root)); // System.out.printf("----- %s ------%n", s(obj)); assert pool.get(root) == null; @@ -364,6 +368,12 @@ IdentityHashMap copies = new IdentityHashMap<>(); copies.put(root, copies); copy0(worklist, copies); + if (DEBUG) { + long duration = System.currentTimeMillis() - start; + if (duration > 10) { + System.out.printf("After copying %s (proxies: %d, pool: %d) %d ms%n", root.getClass().getSimpleName(), proxies.size(), pool.size(), duration); + } + } return pool.get(root); } @@ -382,10 +392,22 @@ public Context() { assert currentContext.get() == null : currentContext.get(); currentContext.set(this); + Context previous = previousContext.get(); + if (previous != null) { + previousPool = previous.pool; + pool = new IdentityHashMap<>(previousPool.size()); + } else { + pool = new IdentityHashMap<>(); + previousPool = null; + } } private final Map proxies = new IdentityHashMap<>(); - private final Map pool = new IdentityHashMap<>(); + private final Map pool; + private final Map previousPool; + + int invocationCacheHits; + int invocationCacheMisses; /** * Gets the value of a given object within this context. @@ -399,7 +421,7 @@ Object proxy = proxies.get(obj); if (proxy == null) { Class[] interfaces = ProxyUtil.getAllInterfaces(obj.getClass()); - proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), interfaces, new Handler<>(obj, this)); + proxy = Proxy.newProxyInstance(obj.getClass().getClassLoader(), interfaces, new Handler<>(obj)); proxies.put(obj, proxy); } return (T) proxy; @@ -421,9 +443,27 @@ } } + private static final boolean DEBUG = Boolean.getBoolean("graal.replayContext.debug"); + public void close() { assert currentContext.get() == this : currentContext.get(); + if (DEBUG) { + int overlap = 0; + if (previousPool != null) { + for (Object key : previousPool.keySet()) { + if (pool.containsKey(key)) { + overlap++; + } + } + } + if (DEBUG) { + System.out.printf("proxies: %d, pool: %d, overlap: %d, invocation cache hits: %d, invocation cache misses: %d%n", proxies.size(), pool.size(), overlap, invocationCacheHits, + invocationCacheMisses); + } + } currentContext.set(null); + previousContext.set(this); + } /** @@ -439,7 +479,7 @@ throw new GraalInternalError("Expecting proxy, found instance of %s", o.getClass()); } } else { - if (!Proxy.isProxyClass(o.getClass())) { + if (Proxy.isProxyClass(o.getClass())) { throw new GraalInternalError("Expecting instance of %s, found proxy", o.getClass()); } } @@ -448,6 +488,30 @@ return true; } + private Leave leave; + int activeInvocations; + + public Leave leave() { + return new Leave(); + } + + public class Leave implements AutoCloseable { + + Leave() { + assert currentContext.get() == Context.this; + assert Context.this.leave == null; + Context.this.leave = this; + currentContext.set(null); + } + + public void close() { + assert leave == this; + assert currentContext.get() == null; + currentContext.set(Context.this); + leave = null; + } + } + /** * Gets the currently active context for the calling thread. * @@ -456,4 +520,14 @@ public static Context getCurrent() { return currentContext.get(); } + + public boolean inProxyInvocation() { + return activeInvocations != 0; + } + + public static void breakpoint() { + if (getCurrent() != null) { + System.console(); + } + } } diff -r 1019a5d7c065 -r 84bef219afc7 graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Handler.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Handler.java Wed Nov 26 23:05:24 2014 +0100 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Handler.java Wed Nov 26 23:11:03 2014 +0100 @@ -24,29 +24,19 @@ import java.lang.reflect.*; import java.util.*; +import java.util.concurrent.*; public class Handler implements InvocationHandler { private final T delegate; - private final Context context; - - Map cachedInvocations = new HashMap<>(); - - public Handler(T delegate, Context context) { - this.delegate = delegate; - this.context = context; - } - public T getDelegate() { - return delegate; - } + /** + * Proxies objects may be visible to multiple threads. + */ + Map cachedInvocations = new ConcurrentHashMap<>(); - static Object unproxifyObject(Object obj) { - if (obj != null && Proxy.isProxyClass(obj.getClass())) { - Handler h = (Handler) Proxy.getInvocationHandler(obj); - return h.delegate; - } - return obj; + public Handler(T delegate) { + this.delegate = delegate; } static Object[] unproxify(Object[] args) { @@ -56,12 +46,23 @@ Object[] res = args; for (int i = 0; i < args.length; i++) { Object arg = args[i]; - if (arg != null && Proxy.isProxyClass(arg.getClass())) { - Handler h = (Handler) Proxy.getInvocationHandler(arg); - if (res == args) { - res = args.clone(); + if (arg != null) { + if (Proxy.isProxyClass(arg.getClass())) { + Handler h = (Handler) Proxy.getInvocationHandler(arg); + if (res == args) { + res = args.clone(); + } + res[i] = h.delegate; + } else if (arg instanceof Object[]) { + Object[] arrayArg = (Object[]) arg; + arrayArg = unproxify(arrayArg); + if (arrayArg != arg) { + if (res == args) { + res = args.clone(); + } + res[i] = arrayArg; + } } - res[i] = h.delegate; } } return res; @@ -72,14 +73,19 @@ return method.getReturnType() != Void.TYPE; } + private static final Object NULL_RESULT = new Object(); + @Override public Object invoke(Object proxy, Method method, Object[] a) throws Throwable { Object[] args = unproxify(a); boolean isCacheable = isCacheable(method); Invocation invocation = new Invocation(method, delegate, args); if (isCacheable) { - if (cachedInvocations.containsKey(invocation)) { - Object result = cachedInvocations.get(invocation); + Object result = cachedInvocations.get(invocation); + if (result != null) { + if (result == NULL_RESULT) { + result = null; + } // System.out.println(invocation + ": " + result); return result; } @@ -87,11 +93,21 @@ // System.out.println("not cacheable: " + method); } - Object result = invocation.invoke(); - result = context.get(result); - if (isCacheable) { - cachedInvocations.put(invocation, result); + Context context = Context.getCurrent(); + assert context != null; + try { + assert context.activeInvocations >= 0; + context.activeInvocations++; + Object result = invocation.invoke(); + result = context.get(result); + if (isCacheable) { + context.invocationCacheHits--; + cachedInvocations.put(invocation, result == null ? NULL_RESULT : result); + } + return result; + } finally { + assert context.activeInvocations > 0; + context.activeInvocations--; } - return result; } }