changeset 22610:0d282ecc09ab

c.o.g.microbenchmarks: add graal jmh benchmarks.
author Josef Eisl <josef.eisl@jku.at>
date Wed, 09 Sep 2015 12:54:12 +0200
parents 805e5bdb291b
children bdb46c461d75
files graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/ArrayDuplicationBenchmark.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/ConditionalEliminationBenchmark.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/FrameStateAssigmentPhaseBenchmark.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/GraalBenchmark.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/GraphCopyBenchmark.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/NodeBenchmark.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/SchedulePhaseBenchmark.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/FrameStateAssignmentState.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/GraalState.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/GraalUtil.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/GraphState.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/MethodSpec.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/NodesState.java graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/ScheduleState.java mx.graal/suite.py
diffstat 15 files changed, 873 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/ArrayDuplicationBenchmark.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,76 @@
+package com.oracle.graal.microbenchmarks.graal;
+
+import java.util.*;
+
+import org.openjdk.jmh.annotations.*;
+
+@State(Scope.Thread)
+public class ArrayDuplicationBenchmark {
+
+    /** How large should the test-arrays be. */
+    private static final int TESTSIZE = 300;
+
+    private Object[][] testObjectArray;
+
+    private Object[] dummy;
+
+    @Setup
+    public void setup() {
+        testObjectArray = new Object[TESTSIZE][];
+        for (int i = 0; i < TESTSIZE; i++) {
+            testObjectArray[i] = new Object[20];
+        }
+    }
+
+    @Setup(Level.Iteration)
+    public void iterationSetup() {
+        dummy = new Object[TESTSIZE * 3];
+    }
+
+    @TearDown(Level.Iteration)
+    public void iterationTearDown() {
+        dummy = null;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(TESTSIZE)
+    public Object[] normalArraycopy() {
+        for (int i = 0, j = 0; i < TESTSIZE; i++) {
+            dummy[j++] = normalArraycopy(testObjectArray[i]);
+        }
+        return dummy;
+    }
+
+    public Object[] normalArraycopy(Object[] cache) {
+        Object[] result = new Object[cache.length];
+        System.arraycopy(cache, 0, result, 0, result.length);
+        return result;
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(TESTSIZE)
+    public Object[] arraysCopyOf() {
+        for (int i = 0, j = 0; i < TESTSIZE; i++) {
+            dummy[j++] = arraysCopyOf(testObjectArray[i]);
+        }
+        return dummy;
+    }
+
+    public Object[] arraysCopyOf(Object[] cache) {
+        return Arrays.copyOf(cache, cache.length);
+    }
+
+    @Benchmark
+    @OperationsPerInvocation(TESTSIZE)
+    public Object[] cloneObjectArray() {
+        for (int i = 0, j = 0; i < TESTSIZE; i++) {
+            dummy[j++] = arraysClone(testObjectArray[i]);
+        }
+        return dummy;
+    }
+
+    public Object[] arraysClone(Object[] cache) {
+        return (Object[]) cache.clone();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/ConditionalEliminationBenchmark.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,103 @@
+package com.oracle.graal.microbenchmarks.graal;
+
+import org.openjdk.jmh.annotations.*;
+
+import com.oracle.graal.microbenchmarks.graal.util.*;
+import com.oracle.graal.phases.common.*;
+
+public class ConditionalEliminationBenchmark extends GraalBenchmark {
+
+    @MethodSpec(declaringClass = ConditionalEliminationBenchmark.class, name = "nullnessSnippet")
+    public static class Nullness extends GraphState {
+    }
+
+    @SuppressWarnings("unused")
+    public static int nullnessSnippet(Object a, Object b) {
+        if (a == null) {
+            if (a == b) {
+                if (b == null) {
+                    return 1;
+                } else {
+                    return -2;
+                }
+            } else {
+                if (b == null) {
+                    return -3;
+                } else {
+                    return 4;
+                }
+            }
+        } else {
+            if (a == b) {
+                if (b == null) {
+                    return -5;
+                } else {
+                    return 6;
+                }
+            } else {
+                if (b == null) {
+                    return 7;
+                } else {
+                    return 8;
+                }
+            }
+        }
+    }
+
+    @Benchmark
+    @Warmup(iterations = 20)
+    public void nullness(Nullness s, GraalState g) {
+        new ConditionalEliminationPhase().apply(s.graph);
+    }
+
+    @MethodSpec(declaringClass = ConditionalEliminationBenchmark.class, name = "searchSnippet")
+    public static class Search extends GraphState {
+    }
+
+    static class Entry {
+        final String name;
+
+        public Entry(String name) {
+            this.name = name;
+        }
+    }
+
+    static class EntryWithNext extends Entry {
+        public EntryWithNext(String name, Entry next) {
+            super(name);
+            this.next = next;
+        }
+
+        final Entry next;
+    }
+
+    public static Entry searchSnippet(Entry start, String name, Entry alternative) {
+        Entry current = start;
+        do {
+            while (current instanceof EntryWithNext) {
+                if (name != null && current.name == name) {
+                    current = null;
+                } else {
+                    Entry next = ((EntryWithNext) current).next;
+                    current = next;
+                }
+            }
+
+            if (current != null) {
+                if (current.name.equals(name)) {
+                    return current;
+                }
+            }
+            if (current == alternative) {
+                return null;
+            }
+            current = alternative;
+
+        } while (true);
+    }
+
+    @Benchmark
+    public void search(Search s, GraalState g) {
+        new ConditionalEliminationPhase().apply(s.graph);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/FrameStateAssigmentPhaseBenchmark.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,20 @@
+package com.oracle.graal.microbenchmarks.graal;
+
+import java.util.*;
+
+import org.openjdk.jmh.annotations.*;
+
+import com.oracle.graal.microbenchmarks.graal.util.*;
+
+@Warmup(iterations = 15)
+public class FrameStateAssigmentPhaseBenchmark extends GraalBenchmark {
+
+    @MethodSpec(declaringClass = StringTokenizer.class, name = "nextToken", parameters = {String.class})
+    public static class StringTokenizedNextToken extends FrameStateAssignmentState {
+    }
+
+    @Benchmark
+    public void nextToken(StringTokenizedNextToken s) {
+        s.phase.apply(s.graph);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/GraalBenchmark.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,22 @@
+package com.oracle.graal.microbenchmarks.graal;
+
+import static com.oracle.graal.microbenchmarks.graal.GraalBenchmark.Defaults.*;
+
+import org.openjdk.jmh.annotations.*;
+
+/**
+ * All classes defining Graal benchmarks must subclass this class as it defines the default value
+ * for each benchmark option. Individual options can be overridden in the subclasses or by an
+ * individual benchmark.
+ */
+@Warmup(iterations = WARMUP_ITERATIONS)
+@Measurement(iterations = MEASUREMENT_ITERATIONS)
+@Fork(FORKS)
+public class GraalBenchmark {
+
+    public static class Defaults {
+        public static final int MEASUREMENT_ITERATIONS = 10;
+        public static final int WARMUP_ITERATIONS = 10;
+        public static final int FORKS = 1;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/GraphCopyBenchmark.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,108 @@
+package com.oracle.graal.microbenchmarks.graal;
+
+import org.openjdk.jmh.annotations.*;
+
+import com.oracle.graal.graph.*;
+import com.oracle.graal.microbenchmarks.graal.util.*;
+import com.oracle.graal.nodes.*;
+
+/**
+ * Benchmarks the performance of {@link Graph#copy()}.
+ */
+public class GraphCopyBenchmark extends GraalBenchmark {
+
+    @MethodSpec(declaringClass = ConditionalEliminationBenchmark.class, name = "nullnessSnippet")
+    public static class Nullness extends GraphState {
+    }
+
+    @SuppressWarnings("unused")
+    public static int nullnessSnippet(Object a, Object b) {
+        if (a == null) {
+            if (a == b) {
+                if (b == null) {
+                    return 1;
+                } else {
+                    return -2;
+                }
+            } else {
+                if (b == null) {
+                    return -3;
+                } else {
+                    return 4;
+                }
+            }
+        } else {
+            if (a == b) {
+                if (b == null) {
+                    return -5;
+                } else {
+                    return 6;
+                }
+            } else {
+                if (b == null) {
+                    return 7;
+                } else {
+                    return 8;
+                }
+            }
+        }
+    }
+
+    @Benchmark
+    @Warmup(iterations = 20)
+    public StructuredGraph nullness(Nullness s, GraalState g) {
+        return (StructuredGraph) s.graph.copy();
+    }
+
+    @MethodSpec(declaringClass = GraphCopyBenchmark.class, name = "searchSnippet")
+    public static class Search extends GraphState {
+    }
+
+    static class Entry {
+        final String name;
+
+        public Entry(String name) {
+            this.name = name;
+        }
+    }
+
+    static class EntryWithNext extends Entry {
+        public EntryWithNext(String name, Entry next) {
+            super(name);
+            this.next = next;
+        }
+
+        final Entry next;
+    }
+
+    public static Entry searchSnippet(Entry start, String name, Entry alternative) {
+        Entry current = start;
+        do {
+            while (current instanceof EntryWithNext) {
+                if (name != null && current.name == name) {
+                    current = null;
+                } else {
+                    Entry next = ((EntryWithNext) current).next;
+                    current = next;
+                }
+            }
+
+            if (current != null) {
+                if (current.name.equals(name)) {
+                    return current;
+                }
+            }
+            if (current == alternative) {
+                return null;
+            }
+            current = alternative;
+
+        } while (true);
+    }
+
+    @Benchmark
+    @Warmup(iterations = 20)
+    public StructuredGraph search(Search s, GraalState g) {
+        return (StructuredGraph) s.graph.copy();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/NodeBenchmark.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,107 @@
+package com.oracle.graal.microbenchmarks.graal;
+
+import java.util.*;
+
+import org.openjdk.jmh.annotations.*;
+import org.openjdk.jmh.infra.Blackhole;
+
+import com.oracle.graal.graph.*;
+import com.oracle.graal.microbenchmarks.graal.util.*;
+import com.oracle.graal.microbenchmarks.graal.util.NodesState.NodePair;
+
+public class NodeBenchmark extends GraalBenchmark {
+
+    @MethodSpec(declaringClass = String.class, name = "equals")
+    public static class StringEquals extends NodesState {
+    }
+
+    @Benchmark
+    @Warmup(iterations = 20)
+    public int getNodeClass(StringEquals s) {
+        int sum = 0;
+        for (Node n : s.nodes) {
+            sum += n.getNodeClass().iterableId();
+        }
+        return sum;
+    }
+
+    @Benchmark
+    public void inputs(StringEquals s, Blackhole bh) {
+        for (Node n : s.nodes) {
+            for (Node input : n.inputs()) {
+                bh.consume(input);
+            }
+        }
+    }
+
+    @Benchmark
+    public void usages(StringEquals s, Blackhole bh) {
+        for (Node n : s.nodes) {
+            for (Node input : n.usages()) {
+                bh.consume(input);
+            }
+        }
+    }
+
+    @MethodSpec(declaringClass = HashMap.class, name = "computeIfAbsent")
+    public static class HashMapComputeIfAbsent extends NodesState {
+    }
+
+    @Benchmark
+    @Warmup(iterations = 20)
+    public void nonNullInputs(HashMapComputeIfAbsent s, Blackhole bh) {
+        for (Node n : s.nodes) {
+            for (Node input : n.inputs().nonNull()) {
+                bh.consume(input);
+            }
+        }
+    }
+
+    @Benchmark
+    @Warmup(iterations = 20)
+    public int valueEquals_STRING_EQUALS(StringEquals s) {
+        int result = 0;
+        for (NodePair np : s.valueEqualsNodePairs) {
+            if (np.n1.valueEquals(np.n2)) {
+                result += 27;
+            } else {
+                result += 31;
+            }
+        }
+        return result;
+    }
+
+    @Benchmark
+    @Warmup(iterations = 20)
+    public int valueEquals_HASHMAP_COMPUTE_IF_ABSENT(HashMapComputeIfAbsent s) {
+        int result = 0;
+        for (NodePair np : s.valueEqualsNodePairs) {
+            if (np.n1.valueEquals(np.n2)) {
+                result += 27;
+            } else {
+                result += 31;
+            }
+        }
+        return result;
+    }
+
+    @Benchmark
+    @Warmup(iterations = 20)
+    public int valueNumberLeaf_HASHMAP_COMPUTE_IF_ABSENT(HashMapComputeIfAbsent s) {
+        int result = 0;
+        for (Node n : s.valueNumberableLeafNodes) {
+            result += (n.getNodeClass().isLeafNode() ? 1 : 0);
+        }
+        return result;
+    }
+
+    @Benchmark
+    @Warmup(iterations = 20)
+    public int valueNumberLeaf_STRING_EQUALS(StringEquals s) {
+        int result = 0;
+        for (Node n : s.valueNumberableLeafNodes) {
+            result += (n.getNodeClass().isLeafNode() ? 1 : 0);
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/SchedulePhaseBenchmark.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,74 @@
+package com.oracle.graal.microbenchmarks.graal;
+
+import java.util.*;
+
+import org.openjdk.jmh.annotations.*;
+
+import com.oracle.graal.microbenchmarks.graal.util.*;
+import com.oracle.graal.phases.schedule.SchedulePhase.SchedulingStrategy;
+
+@Warmup(iterations = 15)
+public class SchedulePhaseBenchmark extends GraalBenchmark {
+
+    @MethodSpec(declaringClass = String.class, name = "equals")
+    public static class StringEquals extends ScheduleState {
+    }
+
+    @Benchmark
+    public void stringEquals(StringEquals s) {
+        s.schedule.apply(s.graph);
+    }
+
+    public static int[] intersectionSnippet(int[] in1, int[] in2) {
+        int[] result = new int[Math.min(in1.length, in2.length)];
+        int next = 0;
+        for (int i1 : in1) {
+            for (int i2 : in2) {
+                if (i2 == i1) {
+                    result[next++] = i1;
+                    break;
+                }
+            }
+        }
+        if (next < result.length) {
+            result = Arrays.copyOf(result, next);
+        }
+        return result;
+    }
+
+    @MethodSpec(declaringClass = SchedulePhaseBenchmark.class, name = "intersectionSnippet")
+    public static class IntersectionState_LATEST_OPTIMAL extends ScheduleState {
+        public IntersectionState_LATEST_OPTIMAL() {
+            super(SchedulingStrategy.LATEST);
+        }
+    }
+
+    @Benchmark
+    public void intersection_LATEST_OPTIMAL(IntersectionState_LATEST_OPTIMAL s) {
+        s.schedule.apply(s.graph);
+    }
+
+    @MethodSpec(declaringClass = SchedulePhaseBenchmark.class, name = "intersectionSnippet")
+    public static class IntersectionState_LATEST_OUT_OF_LOOPS_OPTIMAL extends ScheduleState {
+        public IntersectionState_LATEST_OUT_OF_LOOPS_OPTIMAL() {
+            super(SchedulingStrategy.LATEST_OUT_OF_LOOPS);
+        }
+    }
+
+    @Benchmark
+    public void intersection_LATEST_OUT_OF_LOOPS_OPTIMAL(IntersectionState_LATEST_OUT_OF_LOOPS_OPTIMAL s) {
+        s.schedule.apply(s.graph);
+    }
+
+    @MethodSpec(declaringClass = SchedulePhaseBenchmark.class, name = "intersectionSnippet")
+    public static class IntersectionState_EARLIEST_OPTIMAL extends ScheduleState {
+        public IntersectionState_EARLIEST_OPTIMAL() {
+            super(SchedulingStrategy.EARLIEST);
+        }
+    }
+
+    @Benchmark
+    public void intersection_EARLIEST_OPTIMAL(IntersectionState_EARLIEST_OPTIMAL s) {
+        s.schedule.apply(s.graph);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/FrameStateAssignmentState.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,21 @@
+package com.oracle.graal.microbenchmarks.graal.util;
+
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.phases.common.*;
+
+public class FrameStateAssignmentState extends GraphState {
+
+    public FrameStateAssignmentPhase phase;
+
+    @Override
+    protected StructuredGraph preprocessOriginal(StructuredGraph graph) {
+        new GuardLoweringPhase().apply(graph, null);
+        return graph;
+    }
+
+    @Override
+    public void beforeInvocation() {
+        phase = new FrameStateAssignmentPhase();
+        super.beforeInvocation();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/GraalState.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,27 @@
+package com.oracle.graal.microbenchmarks.graal.util;
+
+import jdk.internal.jvmci.meta.*;
+
+import org.openjdk.jmh.annotations.*;
+
+import com.oracle.graal.api.runtime.*;
+import com.oracle.graal.compiler.target.*;
+import com.oracle.graal.phases.util.*;
+import com.oracle.graal.runtime.*;
+
+/**
+ * Read-only, benchmark-wide state providing Graal runtime context.
+ */
+@State(Scope.Benchmark)
+public class GraalState {
+
+    public final Backend backend;
+    public final Providers providers;
+    public final MetaAccessProvider metaAccess;
+
+    public GraalState() {
+        backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend();
+        providers = backend.getProviders();
+        metaAccess = providers.getMetaAccess();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/GraalUtil.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,117 @@
+package com.oracle.graal.microbenchmarks.graal.util;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import jdk.internal.jvmci.meta.*;
+
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graphbuilderconf.*;
+import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
+import com.oracle.graal.java.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.tiers.*;
+
+public class GraalUtil {
+
+    public static Method getMethod(Class<?> declaringClass, String name, Class<?>... parameterTypes) {
+        try {
+            if (parameterTypes == null) {
+                Method found = null;
+                for (Method m : declaringClass.getDeclaredMethods()) {
+                    if (m.getName().equals(name)) {
+                        if (found != null) {
+                            throw new RuntimeException("more than one method named " + name + " in " + declaringClass);
+                        }
+                        found = m;
+                    }
+                }
+                if (found == null) {
+                    throw new NoSuchMethodException(declaringClass.getName() + "." + name);
+                }
+                return found;
+            } else {
+                return declaringClass.getDeclaredMethod(name, parameterTypes);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Gets the first {@link MethodSpec} annotation encountered in the class hierarchy terminated by
+     * {@code startClass}.
+     */
+    public static MethodSpec getMethodSpec(Class<?> startClass) {
+        Class<?> c = startClass;
+        while (c != null) {
+            MethodSpec methodSpec = c.getAnnotation(MethodSpec.class);
+            if (methodSpec != null) {
+                return methodSpec;
+            }
+            c = c.getSuperclass();
+        }
+        throw new RuntimeException("Could not find class annotated with " + MethodSpec.class.getSimpleName() + " in hierarchy of " + startClass);
+    }
+
+    /**
+     * Gets the method specified by the first {@link MethodSpec} annotation encountered in the class
+     * hierarchy terminated by {@code startClass}.
+     */
+    public static Method getMethodFromMethodSpec(Class<?> startClass) {
+        MethodSpec methodSpec = getMethodSpec(startClass);
+        Class<?> declaringClass = methodSpec.declaringClass();
+        if (declaringClass == MethodSpec.class) {
+            declaringClass = startClass;
+        }
+        String name = methodSpec.name();
+        Class<?>[] parameters = methodSpec.parameters();
+        if (parameters.length == 1 && parameters[0] == void.class) {
+            parameters = null;
+        }
+        return getMethod(declaringClass, name, parameters);
+    }
+
+    /**
+     * Gets the graph for the method specified by the first {@link MethodSpec} annotation
+     * encountered in the class hierarchy terminated by {@code startClass}.
+     */
+    public static StructuredGraph getGraphFromMethodSpec(Class<?> startClass) {
+        MethodSpec methodSpec = getMethodSpec(startClass);
+        Class<?> declaringClass = methodSpec.declaringClass();
+        if (declaringClass == MethodSpec.class) {
+            declaringClass = startClass;
+        }
+        String name = methodSpec.name();
+        Class<?>[] parameters = methodSpec.parameters();
+        if (parameters.length == 1 && parameters[0] == void.class) {
+            parameters = null;
+        }
+        return getGraph(declaringClass, name, parameters);
+    }
+
+    public static StructuredGraph getGraph(Class<?> declaringClass, String name, Class<?>... parameterTypes) {
+        return getGraph(getMethod(declaringClass, name, parameterTypes));
+    }
+
+    public static StructuredGraph getGraph(Method method) {
+        GraalState graal = new GraalState();
+        ResolvedJavaMethod javaMethod = graal.metaAccess.lookupJavaMethod(method);
+        return getGraph(graal, javaMethod);
+    }
+
+    public static StructuredGraph getGraph(GraalState graal, ResolvedJavaMethod javaMethod) {
+        StructuredGraph graph = new StructuredGraph(javaMethod, StructuredGraph.AllowAssumptions.YES);
+        PhaseSuite<HighTierContext> graphBuilderSuite = new PhaseSuite<>();
+        MetaAccessProvider metaAccess = graal.providers.getMetaAccess();
+        graphBuilderSuite.appendPhase(new GraphBuilderPhase(GraphBuilderConfiguration.getDefault(new Plugins(new InvocationPlugins(metaAccess)))));
+        graphBuilderSuite.apply(graph, new HighTierContext(graal.providers, graphBuilderSuite, OptimisticOptimizations.ALL));
+        return graph;
+    }
+
+    public static Node[] getNodes(StructuredGraph graph) {
+        List<Node> nodeList = graph.getNodes().snapshot();
+        return nodeList.toArray(new Node[nodeList.size()]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/GraphState.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,55 @@
+package com.oracle.graal.microbenchmarks.graal.util;
+
+import static com.oracle.graal.microbenchmarks.graal.util.GraalUtil.*;
+import jdk.internal.jvmci.meta.*;
+
+import org.openjdk.jmh.annotations.*;
+
+import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.internal.*;
+import com.oracle.graal.nodes.*;
+
+/**
+ * State providing a new copy of a graph for each invocation of a benchmark. Subclasses of this
+ * class are annotated with {@link MethodSpec} to specify the Java method that will be parsed to
+ * obtain the original graph.
+ */
+@State(Scope.Thread)
+public abstract class GraphState {
+
+    public GraphState() {
+        // Ensure a debug configuration for this thread is initialized
+        if (Debug.isEnabled() && DebugScope.getConfig() == null) {
+            DebugEnvironment.initialize(System.out);
+        }
+
+        GraalState graal = new GraalState();
+        ResolvedJavaMethod method = graal.metaAccess.lookupJavaMethod(getMethodFromMethodSpec(getClass()));
+        StructuredGraph graph = null;
+        try (Debug.Scope s = Debug.scope("GraphState", method)) {
+            graph = preprocessOriginal(getGraph(graal, method));
+        } catch (Throwable t) {
+            Debug.handle(t);
+        }
+        this.originalGraph = graph;
+    }
+
+    protected StructuredGraph preprocessOriginal(StructuredGraph graph) {
+        return graph;
+    }
+
+    /**
+     * Original graph from which the per-benchmark invocation {@link #graph} is cloned.
+     */
+    private final StructuredGraph originalGraph;
+
+    /**
+     * The graph processed by the benchmark.
+     */
+    public StructuredGraph graph;
+
+    @Setup(Level.Invocation)
+    public void beforeInvocation() {
+        graph = (StructuredGraph) originalGraph.copy();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/MethodSpec.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,27 @@
+package com.oracle.graal.microbenchmarks.graal.util;
+
+import java.lang.annotation.*;
+
+/**
+ * Annotation for specifying a method based on a declaring class, a name and parameter types.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface MethodSpec {
+    /**
+     * The class in which the method is declared. If not specified, the annotated class is used as
+     * the declaring class.
+     */
+    Class<?> declaringClass() default MethodSpec.class;
+
+    /**
+     * The name of the method.
+     */
+    String name();
+
+    /**
+     * The types of the method's parameters. If not specified, then the {@linkplain #name() name} is
+     * expected to be unique within the declaring class.
+     */
+    Class<?>[] parameters() default {void.class};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/NodesState.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,77 @@
+package com.oracle.graal.microbenchmarks.graal.util;
+
+import static com.oracle.graal.microbenchmarks.graal.util.GraalUtil.*;
+
+import java.util.*;
+
+import org.openjdk.jmh.annotations.*;
+
+import com.oracle.graal.graph.*;
+
+/**
+ * State providing the nodes in a graph. Subclasses of this class are annotated with
+ * {@link MethodSpec} to specify the Java method that will be parsed to obtain the original graph.
+ */
+@State(Scope.Benchmark)
+public abstract class NodesState {
+
+    public NodesState() {
+        this.nodes = getNodes(getGraphFromMethodSpec(getClass()));
+        this.originalNodes = nodes.clone();
+        List<Node> vnln = new ArrayList<>(nodes.length);
+        List<NodePair> list2 = new ArrayList<>(nodes.length);
+        for (int i = 0; i < nodes.length; i++) {
+            Node n = nodes[i];
+            NodeClass<?> nc = n.getNodeClass();
+            if (nc.valueNumberable() && nc.isLeafNode()) {
+                vnln.add(n);
+            }
+            for (int j = i + i; j < nodes.length; j++) {
+                Node o = nodes[j];
+                if (o.getClass() == n.getClass()) {
+                    list2.add(new NodePair(n, o));
+                }
+            }
+        }
+        valueNumberableLeafNodes = vnln.toArray(new Node[vnln.size()]);
+        valueEqualsNodePairs = list2.toArray(new NodePair[list2.size()]);
+    }
+
+    /**
+     * Used to check that benchmark does not mutate {@link #nodes}.
+     */
+    private final Node[] originalNodes;
+
+    /**
+     * The nodes processed by the benchmark. These arrays must be treated as read-only within the
+     * benchmark method.
+     */
+    public final Node[] nodes;
+    public final Node[] valueNumberableLeafNodes;
+    public final NodePair[] valueEqualsNodePairs;
+
+    public final class NodePair {
+        public final Node n1;
+        public final Node n2;
+
+        public NodePair(Node n1, Node n2) {
+            this.n1 = n1;
+            this.n2 = n2;
+        }
+    }
+
+    private int invocation;
+
+    @TearDown(Level.Invocation)
+    public void afterInvocation() {
+        if (invocation == 0) {
+            // Only need to check the first invocation
+            invocation++;
+            for (int i = 0; i < nodes.length; i++) {
+                if (nodes[i] != originalNodes[i]) {
+                    throw new InternalError(String.format("Benchmark method mutated node %d: original=%s, current=%s", i, originalNodes[i], nodes[i]));
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.microbenchmarks/src/com/oracle/graal/microbenchmarks/graal/util/ScheduleState.java	Wed Sep 09 12:54:12 2015 +0200
@@ -0,0 +1,25 @@
+package com.oracle.graal.microbenchmarks.graal.util;
+
+import com.oracle.graal.phases.schedule.*;
+import com.oracle.graal.phases.schedule.SchedulePhase.SchedulingStrategy;
+
+public class ScheduleState extends GraphState {
+
+    public SchedulePhase schedule;
+
+    private final SchedulingStrategy selectedStrategy;
+
+    public ScheduleState(SchedulingStrategy selectedStrategy) {
+        this.selectedStrategy = selectedStrategy;
+    }
+
+    public ScheduleState() {
+        this(SchedulingStrategy.LATEST_OUT_OF_LOOPS);
+    }
+
+    @Override
+    public void beforeInvocation() {
+        schedule = new SchedulePhase(selectedStrategy);
+        super.beforeInvocation();
+    }
+}
--- a/mx.graal/suite.py	Wed Sep 09 13:29:51 2015 +0200
+++ b/mx.graal/suite.py	Wed Sep 09 12:54:12 2015 +0200
@@ -604,6 +604,20 @@
       "workingSets" : "Graal,Bench",
     },
 
+    "com.oracle.graal.microbenchmarks" : {
+      "subDir" : "graal",
+      "sourceDirs" : ["src"],
+      "dependencies" : [
+        "JMH",
+        "com.oracle.graal.java",
+        "com.oracle.graal.runtime",
+      ],
+      "checkstyle" : "com.oracle.graal.graph",
+      "javaCompliance" : "1.8",
+      "annotationProcessors" : ["JMH"],
+      "workingSets" : "Graal,Bench",
+    },
+
     "com.oracle.graal.loop" : {
       "subDir" : "graal",
       "sourceDirs" : ["src"],