Mercurial > hg > gc-bench
view src/main/java/org/openjdk/gcbench/GCBench.java @ 91:e8e80f26edfb
Sanity test for checking non-allocating performance.
author | shade |
---|---|
date | Thu, 23 Nov 2017 15:23:31 +0100 |
parents | 583fef4276f5 |
children |
line wrap: on
line source
/* * Copyright (c) 2017, Red Hat Inc. 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * 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 org.openjdk.gcbench; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; import org.openjdk.gcbench.tests.*; import org.openjdk.jmh.annotations.Threads; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.*; import org.openjdk.jmh.util.Utils; import org.openjdk.jmh.util.Version; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; public class GCBench { private final PrintWriter pw; private Options baseOpts; private String testFilter; private List<Test> tests; public static void main(String... args) throws RunnerException, IOException { try { GCBench bench = new GCBench(args); bench.run(); } catch (HelpPrintException e) { // do nothing } } public GCBench(String[] args) throws RunnerException, IOException, HelpPrintException { pw = new PrintWriter(System.out, true); pw.println("GC Benchmarks Suite"); pw.println("----------------------------------------------------------------------------------------------------------"); pw.println(); pw.println("# " + Version.getVersion()); pw.println("# " + Utils.getCurrentJvmVersion()); pw.println("# " + Utils.getCurrentOSVersion()); pw.println(); Utils.reflow(pw, "These tests assess the garbage collector performance. These tests implicitly test other aspects of system " + "under test, including hardware, OS and JVM tuning. Some tests may implicitly test the testing infrastructure " + "itself, although the benchmark code tries to reduce the infrastructural impact as much as possible.", 80, 2); pw.println(); Utils.reflow(pw, "If you are sharing this report, please share it in full, including the JVM version, OS flavor and version, " + "plus some data on used hardware.", 80, 2); pw.println(); pw.println(" Use -h to get help on available options."); pw.println(); OptionParser parser = new OptionParser(); parser.formatHelpWith(new OptionFormatter()); OptionSpec<String> optTests = parser.accepts("t", "Test names.") .withRequiredArg().ofType(String.class).describedAs("regexp") .defaultsTo(".*"); OptionSpec<Mode> optMode = parser.accepts("m", "Running mode, one of " + Arrays.toString(Mode.values()) + ".") .withRequiredArg().ofType(Mode.class).describedAs("mode") .defaultsTo(Mode.normal); OptionSpec<Integer> optMaxHeap = parser.accepts("maxHeap", "Max heap to be used for tests. Leave unset to enable auto-detection") .withRequiredArg().ofType(Integer.class).describedAs("MB"); OptionSpec<Integer> optMinHeap = parser.accepts("minHeap", "Min heap to be used for tests.") .withRequiredArg().ofType(Integer.class).describedAs("MB"); OptionSpec<Integer> optMinThreads = parser.accepts("minThreads", "Min threads to be used for tests.") .withRequiredArg().ofType(Integer.class).describedAs("#").defaultsTo(1); OptionSpec<Integer> optMaxThreads = parser.accepts("maxThreads", "Max threads to be used for tests. Leave unset to enable auto-detection.") .withRequiredArg().ofType(Integer.class).describedAs("#"); parser.accepts("h", "Print help."); OptionSet set = parser.parse(args); if (set.has("h")) { parser.printHelpOn(System.out); throw new HelpPrintException(); } final int MIN_THREADS = set.valueOf(optMinThreads); final int MAX_THREADS; if (set.has(optMaxThreads)) { MAX_THREADS = optMaxThreads.value(set); } else { MAX_THREADS = Runtime.getRuntime().availableProcessors()*2; } List<Integer> threads = new ArrayList<>(); threads.add(1); threads.add(Runtime.getRuntime().availableProcessors()/2); threads.add(Runtime.getRuntime().availableProcessors()); threads.add(Runtime.getRuntime().availableProcessors()*2); threads.removeIf(i -> i < MIN_THREADS || i > MAX_THREADS); int minHeap; if (set.has(optMinHeap)) { minHeap = set.valueOf(optMinHeap); } else { minHeap = Runtime.getRuntime().availableProcessors() * 1024; // 1 GB per thread } if (set.has(optMaxHeap)) { HeapSizeManager.override(minHeap, set.valueOf(optMaxHeap)); } else { pw.println("===== Calibrating max heap size"); pw.println(); HeapSizeManager.init(minHeap, pw); } Options opts = new OptionsBuilder() .detectJvmArgs() .threads(Threads.MAX) .verbosity(VerboseMode.SILENT) .shouldFailOnError(true) .build(); int targetAllocRate_MbPerSec = 3000; switch (set.valueOf(optMode)) { case flash: opts = new OptionsBuilder() .parent(opts) .warmupIterations(3) .warmupTime(TimeValue.seconds(1)) .measurementIterations(1) .measurementTime(TimeValue.milliseconds(300)) .forks(1) .build(); break; case quick: opts = new OptionsBuilder() .parent(opts) .warmupIterations(3) .warmupTime(TimeValue.seconds(1)) .measurementIterations(1) .measurementTime(TimeValue.milliseconds(Math.max(1000, (HeapSizeManager.MAX_HEAP * 1000 / targetAllocRate_MbPerSec)))) .forks(1) .build(); break; case normal: opts = new OptionsBuilder() .parent(opts) .warmupIterations(3) .warmupTime(TimeValue.seconds(1)) .measurementIterations(1) .measurementTime(TimeValue.milliseconds(Math.max(1000, 3*(HeapSizeManager.MAX_HEAP * 1000 / targetAllocRate_MbPerSec)))) .forks(3) .build(); break; case tough: opts = new OptionsBuilder() .parent(opts) .warmupIterations(3) .warmupTime(TimeValue.seconds(1)) .measurementIterations(1) .measurementTime(TimeValue.seconds(Math.max(1000, 10*(HeapSizeManager.MAX_HEAP * 1000 / targetAllocRate_MbPerSec)))) .forks(5) .build(); break; default: throw new IllegalStateException(); } baseOpts = opts; testFilter = set.valueOf(optTests); tests = new ArrayList<>(); // alloc family { String groupDescr = "This tests no other activity happens except for the test itself. " + "Runs the active non-allocating test."; tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.sanity.DoNothing.class, "sanity", groupDescr, Dimensions.threads(Sequence.predefined(threads)), Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)) )); } // alloc family { String groupDescr = "Allocates the objects in almost completely empty heap as fast as it can. " + "This tests what allocation pressure can the collector withstand without doing anything else. "; tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.alloc.plain.Objects.class, "alloc.peak.object", groupDescr + "Allocates plain Java Objects.", Dimensions.threads(Sequence.predefined(threads)), Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.alloc.plain.PrimArray.class, "alloc.peak.intarray", groupDescr + "Allocates int[] arrays of different sizes.", Dimensions.threads(Sequence.predefined(threads)), Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen(1, 10_000_000)) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.alloc.plain.RefArray.class, "alloc.peak.refarray", groupDescr + "Allocates Object[] arrays of different sizes.", Dimensions.threads(Sequence.predefined(threads)), Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen(1, 10_000_000)) )); } { String groupDescr = "Allocates the uninitialized arrays in almost completely empty heap as fast as it can. " + "This is the torturous test that allocates more objects than any pure Java application can do, and " + "stresses the allocation paths in collector. "; tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.alloc.uninit.IntArray.class, "alloc.uninit.intarray", groupDescr + "Allocates uninitialized int[] arrays of different sizes.", Dimensions.threads(Sequence.predefined(threads)), Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen(1, 10_000)) )); } if (false) { // TODO: Enable back once they are important again String groupDescr = "Allocates the objects in almost completely empty heap, with rate limiting. " + "If the collector needs some headroom to do cleanup, rate limited tests would show how much " + "headroom is needed. "; tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.alloc.ratelimited.Objects.class, "alloc.rated.object", groupDescr + "Allocates plain Java Objects.", true, Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.alloc.ratelimited.PrimArray.class, "alloc.rated.intarray", groupDescr + "Allocates int[] arrays of different sizes.", true, Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen(1, 10_000_000)) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.alloc.ratelimited.RefArray.class, "alloc.rated.refarray", groupDescr + "Allocates Object[] arrays of different sizes.", true, Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen(1, 10_000_000)) )); } { String groupDesc = "Allocates the objects in heap, when heap is not empty. This tests how well the collector " + "can withstand the allocation pressure when there is other potential work to do. "; tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.retain.RefArray.class, "retain.array", groupDesc + "Retains a reference array of given size.", Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen_Sub(100_000, 100_000_000)) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.retain.LinkedLists.class, "retain.linkedlist", groupDesc + "Retains a linked list of given size.", Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen_Sub(100_000, 100_000_000)) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.retain.Tree.class, "retain.tree", groupDesc + "Retains a tree of given size.", Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen_Sub(100_000, 100_000_000)) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.retain.Tree.class, "retain.hashmap", groupDesc + "Retains a HashMap of given size.", Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen_Sub(100_000, 100_000_000)) )); } { String groupDescr = "Populates heap with data, and randomly overwrites the part of it to cause fragmentation. " + "The tests are rate-limited to see on what load the collector breaks down. "; tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.fragger.ArrayFragger.class, "fragger.array", groupDescr + "Retains a single large reference array.", true, Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.lds(4) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.fragger.TreeFragger.class, "fragger.tree", groupDescr + "Retains a binary tree of Nodes.", true, Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.lds(4) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.fragger.LinkedListFragger.class, "fragger.linkedlist", groupDescr + "Retains a LinkedList.", true, Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.lds(4) )); } { String groupDescr = "Does the allocations, but retains each ${size}-th allocated object. "; tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.sieve.Objects.class, "sieve.object", groupDescr + "Allocates Java Objects.", Dimensions.heapSize(Sequence.powersOfTwo_WithMax(HeapSizeManager.MIN_HEAP, HeapSizeManager.MAX_HEAP)), Dimensions.size(Sequence.powersOfTen_Sub(1, 10_000)) )); } { String groupDescr = "Stresses the application root set. Beefs up the particular part of root set, " + "and then runs peak allocation tests to see if it affects garbage collection. "; tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.roots.Strings.class, "roots.strings", groupDescr + "Allocates and retains a number of interned Strings.", Dimensions.heapSize(Sequence.predefined(HeapSizeManager.MIN_HEAP)), Dimensions.size(Sequence.powersOfTen_Sub(100, 100000)) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.roots.Synchronizers.class, "roots.synchronizers", groupDescr + "Inflates and retains a number of synchronized objects per each thread.", Dimensions.heapSize(Sequence.predefined(HeapSizeManager.MIN_HEAP)), Dimensions.size(Sequence.powersOfTen_Sub(100, 100000)) )); tests.add(new DimensionalTest(baseOpts, org.openjdk.gcbench.roots.Locals.class, "roots.locals", groupDescr + "Produces lots of intermediate local variables on thread stacks.", Dimensions.heapSize(Sequence.predefined(HeapSizeManager.MIN_HEAP)), Dimensions.size(Sequence.powersOfTen_Sub(100, 100000)) )); } { String groupDescr = "Estimates the runtime overhead for reads. These costs include all the infrastructural " + "overheads too, and therefore the score differences matter, not their absolute values. This test " + "is single-threaded. "; tests.add(new MachineCntTest(baseOpts, org.openjdk.gcbench.runtime.reads.Plain.class, "runtime.reads.plain", groupDescr + "Reads plain Java fields.", Dimensions.jvmMode(), Dimensions.javaType() )); tests.add(new MachineCntTest(baseOpts, org.openjdk.gcbench.runtime.reads.Volatile.class, "runtime.reads.volatile", groupDescr + "Reads volatile Java fields.", Dimensions.jvmMode(), Dimensions.javaType() )); } { String groupDescr = "Estimates the runtime overhead for reads. These costs include all the infrastructural " + "overheads too, and therefore the score differences matter, not their absolute values. This test " + "is single-threaded. "; tests.add(new MachineCntTest(baseOpts, org.openjdk.gcbench.runtime.writes.Plain.class, "runtime.writes.plain", groupDescr + "Writes plain Java fields.", Dimensions.jvmMode(), Dimensions.javaType() )); tests.add(new MachineCntTest(baseOpts, org.openjdk.gcbench.runtime.writes.Volatile.class, "runtime.writes.volatile", groupDescr + "Writes volatile Java fields.", Dimensions.jvmMode(), Dimensions.javaType() )); } } public enum Mode { flash, quick, normal, tough, } public void run() throws RunnerException { pw.println(" Available tests: "); for (Test test : tests) { pw.printf(" %-20s %n", test.label()); } pw.println(); Pattern pattern = Pattern.compile(testFilter); List<Test> runTests = new ArrayList<>(); pw.println(" Matching tests:"); for (Test test : tests) { if (test.label().equals("sanity") || pattern.matcher(test.label()).find()) { pw.printf(" %-20s%n", test.label()); runTests.add(test); } } pw.println(); for (Test test : runTests) { test.run(); } } private class HelpPrintException extends Throwable { } }