changeset 5308:820fce52a244

moved GraphCache to platform specific part, solved class unloading problem (see comments in HotSpotGraphCache.java)
author Lukas Stadler <lukas.stadler@jku.at>
date Thu, 26 Apr 2012 14:18:17 +0200
parents 2558ff0945f8
children 19ed2e2391a0
files graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/graph/GraphCache.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/Compiler.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilerImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotGraphCache.java
diffstat 6 files changed, 175 insertions(+), 182 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/graph/GraphCache.java	Wed Apr 25 14:57:40 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,174 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.compiler.graph;
-
-import java.io.*;
-import java.util.*;
-import java.util.Map.Entry;
-import java.util.concurrent.*;
-import java.util.concurrent.atomic.*;
-
-import com.oracle.graal.compiler.*;
-import com.oracle.graal.cri.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.max.cri.ri.*;
-
-public class GraphCache implements RiGraphCache {
-
-    private static final PrintStream out = System.out;
-    private final boolean dump;
-    private boolean enabled = true;
-
-    private final AtomicLong hitCounter = new AtomicLong();
-    private final AtomicLong missCounter = new AtomicLong();
-    private final AtomicLong removeHitCounter = new AtomicLong();
-    private final AtomicLong removeMissCounter = new AtomicLong();
-    private final AtomicLong putCounter = new AtomicLong();
-
-    private class LRUCache extends LinkedHashMap<RiResolvedMethod, Long> {
-        private static final long serialVersionUID = -3973307040793397840L;
-
-        public LRUCache(int initialCapacity) {
-            super(initialCapacity * 2, 0.75f, false);
-        }
-        @Override
-        protected boolean removeEldestEntry(Entry<RiResolvedMethod, Long> eldest) {
-            if (size() > GraalOptions.GraphCacheSize) {
-                graphs.remove(eldest.getValue());
-                cachedGraphIds.remove(eldest.getKey());
-                return true;
-            } else {
-                return false;
-            }
-        }
-
-    }
-
-    private final Map<RiResolvedMethod, Long> currentGraphIds = Collections.synchronizedMap(new LRUCache(GraalOptions.GraphCacheSize));
-
-    private final ConcurrentHashMap<Long, StructuredGraph> graphs = new ConcurrentHashMap<>();
-    private final ConcurrentHashMap<Long, RiResolvedMethod> cachedGraphIds = new ConcurrentHashMap<>();
-
-
-    public GraphCache(boolean dump) {
-        this.dump = dump;
-
-        if (dump) {
-            Runtime.getRuntime().addShutdownHook(new Thread() {
-                @Override
-                public void run() {
-                    out.println("put: " + putCounter.get());
-                    out.println("get hit: " + hitCounter.get());
-                    out.println("get miss: " + missCounter.get());
-                    out.println("remove hit: " + removeHitCounter.get());
-                    out.println("remove miss: " + removeMissCounter.get());
-                }
-            });
-        }
-    }
-
-    public void enable() {
-        enabled = true;
-    }
-
-    @Override
-    public StructuredGraph get(RiResolvedMethod method) {
-        if (!enabled) {
-            return null;
-        }
-        Long currentId = currentGraphIds.get(method);
-        StructuredGraph result = null;
-        if (currentId != null) {
-            result = graphs.get(currentId);
-        }
-
-        if (dump) {
-            if (result == null) {
-                missCounter.incrementAndGet();
-            } else {
-                hitCounter.incrementAndGet();
-            }
-//            if (result == null) {
-//                out.println("miss: " + missCounter.incrementAndGet() + " " + method);
-//            } else {
-//                out.println("hit: " + hitCounter.incrementAndGet() + " " + method);
-//            }
-        }
-        return result;
-    }
-
-    @Override
-    public void put(StructuredGraph graph) {
-        if (!enabled) {
-            return;
-        }
-        assert graph.method() != null;
-        Long currentId = currentGraphIds.get(graph.method());
-        if (currentId != null) {
-            graphs.remove(currentId);
-            cachedGraphIds.remove(currentId);
-        }
-        currentGraphIds.put(graph.method(), graph.graphId());
-        cachedGraphIds.put(graph.graphId(), graph.method());
-        graphs.put(graph.graphId(), graph);
-
-        if (dump) {
-            putCounter.incrementAndGet();
-//            out.println("put: " + putCounter.incrementAndGet() + " (size: " + graphs.size() + ")");
-        }
-    }
-
-    public void clear() {
-        graphs.clear();
-        currentGraphIds.clear();
-        cachedGraphIds.clear();
-        hitCounter.set(0);
-        missCounter.set(0);
-        removeHitCounter.set(0);
-        removeMissCounter.set(0);
-        putCounter.set(0);
-    }
-
-    public void removeGraphs(long[] deoptedGraphs) {
-        for (long graphId : deoptedGraphs) {
-            graphs.remove(graphId);
-            RiResolvedMethod method = cachedGraphIds.get(graphId);
-            if (method != null) {
-                cachedGraphIds.remove(graphId);
-                currentGraphIds.remove(method);
-            }
-            if (dump) {
-                if (method != null) {
-                    removeHitCounter.incrementAndGet();
-                } else {
-                    removeMissCounter.incrementAndGet();
-                }
-//                if (method != null) {
-//                    out.println("remove hit: " + removeHitCounter.incrementAndGet() + " (" + graphId + " " + method + ")");
-//                } else {
-//                    out.println("remove miss: " + removeMissCounter.incrementAndGet() + " (" + graphId + ")");
-//                }
-            }
-        }
-    }
-}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Wed Apr 25 14:57:40 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Thu Apr 26 14:18:17 2012 +0200
@@ -26,7 +26,6 @@
 
 import com.oracle.graal.compiler.*;
 import com.oracle.graal.compiler.phases.*;
-import com.oracle.graal.cri.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.hotspot.ri.*;
 import com.oracle.graal.nodes.*;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/Compiler.java	Wed Apr 25 14:57:40 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/Compiler.java	Thu Apr 26 14:18:17 2012 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.hotspot;
 
 import com.oracle.graal.compiler.*;
-import com.oracle.graal.compiler.graph.*;
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.ri.*;
 import com.oracle.max.cri.ci.*;
@@ -38,7 +37,7 @@
     HotSpotVMConfig getConfig();
     HotSpotRuntime getRuntime();
     CiTarget getTarget();
-    GraphCache getCache();
+    HotSpotGraphCache getCache();
     void evictDeoptedGraphs();
 
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilerImpl.java	Wed Apr 25 14:57:40 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilerImpl.java	Thu Apr 26 14:18:17 2012 +0200
@@ -27,7 +27,6 @@
 import java.net.*;
 
 import com.oracle.graal.compiler.*;
-import com.oracle.graal.compiler.graph.*;
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.cri.*;
 import com.oracle.graal.hotspot.bridge.*;
@@ -93,7 +92,7 @@
     private HotSpotRuntime runtime;
     private GraalCompiler compiler;
     private CiTarget target;
-    private volatile GraphCache cache;
+    private volatile HotSpotGraphCache cache;
 
     private final HotSpotVMConfig config;
 
@@ -180,14 +179,14 @@
 
             compiler = new GraalCompiler(getRuntime(), getTarget(), backend, generator);
             if (GraalOptions.CacheGraphs) {
-                cache = new GraphCache(GraalOptions.PrintGraphCache);
+                cache = new HotSpotGraphCache();
             }
         }
         return compiler;
     }
 
     @Override
-    public GraphCache getCache() {
+    public HotSpotGraphCache getCache() {
         return cache;
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Wed Apr 25 14:57:40 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Thu Apr 26 14:18:17 2012 +0200
@@ -208,7 +208,6 @@
         TTY.println(" in %d ms", System.currentTimeMillis() - startTime);
         if (compiler.getCache() != null) {
             compiler.getCache().clear();
-            compiler.getCache().enable();
         }
         System.gc();
         CiCompilationStatistics.clear("bootstrap2");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotGraphCache.java	Thu Apr 26 14:18:17 2012 +0200
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hotspot.ri;
+
+import java.io.*;
+import java.lang.ref.*;
+import java.util.*;
+import java.util.Map.Entry;
+
+import com.oracle.graal.compiler.*;
+import com.oracle.graal.cri.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.max.cri.ri.*;
+
+/**
+ * This class implements the graph caching system for the HotSpot platform.
+ *
+ * This implementation does not use a map to store the actual cached graphs. The problem is that such maps keep the
+ * graph, and therefore the RiResolvedMethod referenced from the graph, alive. For some applications and benchmarks this
+ * is a problem, e.g., the DaCapoScala "scalatest" benchmark will quickly run out of perm gen because of this.
+ *
+ * This cannot be solved with a WeakHashMap<RiResolvedMethod, Graph>, since the values within the map will keep the keys
+ * alive. In order for this to work we would require a weak map in which the "strongness" of the value references
+ * depends upon the reachability of the keys.
+ *
+ * Therefore the graph cache is implemented in such a way that it stores its cache entries within the RiResolvedMethod.
+ * It uses the {@link RiResolvedMethod#compilerStorage()} map with the HotSpotGraphCache instance as key.
+ * The cached graph will be kept alive as long as the RiResolvedMethod is alive, but does not prevent the method, and
+ * therefore the class, from being unloaded.
+ *
+ * The {@link #cachedGraphIds} map is used to find the graphs that should be removed because of deoptimization, and to
+ * enforce the graph cache size restriction.
+ */
+public class HotSpotGraphCache implements RiGraphCache {
+
+    private static final PrintStream out = System.out;
+
+    private volatile long hitCounter;
+    private volatile long missCounter;
+    private volatile long removeHitCounter;
+    private volatile long removeCounter;
+    private volatile long putCounter;
+
+    /**
+     * An ordered hash map for looking up the methods corresponding to a specific graph id. It enforces the maximum
+     * graph cache size by removing the oldest (in insertion-order) element if the cache gets too big.
+     */
+    private final class LRUCache extends LinkedHashMap<Long, WeakReference<RiResolvedMethod>> {
+
+        private static final long serialVersionUID = -3973307040793397840L;
+
+        public LRUCache() {
+            super(GraalOptions.GraphCacheSize * 2, 0.75f, false);
+        }
+
+        @Override
+        protected boolean removeEldestEntry(Entry<Long, WeakReference<RiResolvedMethod>> eldest) {
+            if (size() > GraalOptions.GraphCacheSize) {
+                RiResolvedMethod method = eldest.getValue().get();
+                if (method != null) {
+                    StructuredGraph cachedGraph = (StructuredGraph) method.compilerStorage().get(HotSpotGraphCache.this);
+                    if (cachedGraph != null && cachedGraph.graphId() == eldest.getKey()) {
+                        method.compilerStorage().remove(HotSpotGraphCache.this);
+                    }
+                }
+                return true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    private final Map<Long, WeakReference<RiResolvedMethod>> cachedGraphIds = Collections.synchronizedMap(new LRUCache());
+
+    public HotSpotGraphCache() {
+        if (GraalOptions.PrintGraphCache) {
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+
+                @Override
+                public void run() {
+                    out.println("put: " + putCounter);
+                    out.println("get hit: " + hitCounter);
+                    out.println("get miss: " + missCounter);
+                    out.println("remove hit: " + removeHitCounter);
+                    out.println("remove miss: " + (removeCounter - removeHitCounter));
+                }
+            });
+        }
+    }
+
+    @Override
+    public StructuredGraph get(RiResolvedMethod method) {
+        StructuredGraph result = (StructuredGraph) method.compilerStorage().get(this);
+
+        if (GraalOptions.PrintGraphCache) {
+            if (result == null) {
+                missCounter++;
+            } else {
+                hitCounter++;
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public void put(StructuredGraph graph) {
+        assert graph.method() != null;
+        cachedGraphIds.put(graph.graphId(), new WeakReference<>(graph.method()));
+        graph.method().compilerStorage().put(this, graph);
+
+        if (GraalOptions.PrintGraphCache) {
+            putCounter++;
+        }
+    }
+
+    public void clear() {
+        synchronized (cachedGraphIds) {
+            for (WeakReference<RiResolvedMethod> ref : cachedGraphIds.values()) {
+                RiResolvedMethod method = ref.get();
+                if (method != null) {
+                    method.compilerStorage().remove(this);
+                }
+            }
+            cachedGraphIds.clear();
+            hitCounter = 0;
+            missCounter = 0;
+            removeHitCounter = 0;
+            removeCounter = 0;
+            putCounter = 0;
+        }
+    }
+
+    public void removeGraphs(long[] deoptedGraphs) {
+        for (long graphId : deoptedGraphs) {
+            WeakReference<RiResolvedMethod> ref = cachedGraphIds.get(graphId);
+            RiResolvedMethod method = ref == null ? null : ref.get();
+            if (method != null) {
+                StructuredGraph cachedGraph = (StructuredGraph) method.compilerStorage().get(this);
+                if (cachedGraph != null && cachedGraph.graphId() == graphId) {
+                    method.compilerStorage().remove(this);
+                    if (GraalOptions.PrintGraphCache) {
+                        removeHitCounter++;
+                    }
+                }
+            }
+            if (GraalOptions.PrintGraphCache) {
+                removeCounter++;
+            }
+        }
+    }
+}