# HG changeset patch # User Lukas Stadler # Date 1335442697 -7200 # Node ID 820fce52a244910fa2489241c401b54e19362c71 # Parent 2558ff0945f8ac0eab627cfc4bc4e5e8e6b80b42 moved GraphCache to platform specific part, solved class unloading problem (see comments in HotSpotGraphCache.java) diff -r 2558ff0945f8 -r 820fce52a244 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/graph/GraphCache.java --- 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 { - private static final long serialVersionUID = -3973307040793397840L; - - public LRUCache(int initialCapacity) { - super(initialCapacity * 2, 0.75f, false); - } - @Override - protected boolean removeEldestEntry(Entry eldest) { - if (size() > GraalOptions.GraphCacheSize) { - graphs.remove(eldest.getValue()); - cachedGraphIds.remove(eldest.getKey()); - return true; - } else { - return false; - } - } - - } - - private final Map currentGraphIds = Collections.synchronizedMap(new LRUCache(GraalOptions.GraphCacheSize)); - - private final ConcurrentHashMap graphs = new ConcurrentHashMap<>(); - private final ConcurrentHashMap 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 + ")"); -// } - } - } - } -} diff -r 2558ff0945f8 -r 820fce52a244 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java --- 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.*; diff -r 2558ff0945f8 -r 820fce52a244 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/Compiler.java --- 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(); } diff -r 2558ff0945f8 -r 820fce52a244 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilerImpl.java --- 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; } diff -r 2558ff0945f8 -r 820fce52a244 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java --- 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"); diff -r 2558ff0945f8 -r 820fce52a244 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotGraphCache.java --- /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, 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> { + + private static final long serialVersionUID = -3973307040793397840L; + + public LRUCache() { + super(GraalOptions.GraphCacheSize * 2, 0.75f, false); + } + + @Override + protected boolean removeEldestEntry(Entry> 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> 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 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 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++; + } + } + } +}