changeset 18561:84bef219afc7

speed up replay compilation testing by re-using object pool across replay contexts
author Doug Simon <doug.simon@oracle.com>
date Wed, 26 Nov 2014 23:11:03 +0100
parents 1019a5d7c065
children 4f27e4a4b4c5
files graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Context.java graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/remote/Handler.java
diffstat 2 files changed, 123 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- 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<Context> currentContext = new ThreadLocal<>();
+    private static final ThreadLocal<Context> previousContext = new ThreadLocal<>();
 
     private final Map<Class<?>, 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<Object, Object> 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<Object, Object> proxies = new IdentityHashMap<>();
-    private final Map<Object, Object> pool = new IdentityHashMap<>();
+    private final Map<Object, Object> pool;
+    private final Map<Object, Object> 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();
+        }
+    }
 }
--- 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<T> implements InvocationHandler {
 
     private final T delegate;
-    private final Context context;
-
-    Map<Invocation, Object> 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<Invocation, Object> 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;
     }
 }