Path: blob/master/test/jdk/java/util/Collection/RemoveMicroBenchmark.java
41149 views
/*1* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @summary micro-benchmark correctness mode26* @run main RemoveMicroBenchmark iterations=1 size=8 warmup=027*/2829import static java.util.concurrent.TimeUnit.MILLISECONDS;30import static java.util.stream.Collectors.toCollection;3132import java.lang.ref.ReferenceQueue;33import java.lang.ref.WeakReference;34import java.util.ArrayDeque;35import java.util.ArrayList;36import java.util.Collection;37import java.util.Collections;38import java.util.Deque;39import java.util.HashMap;40import java.util.Iterator;41import java.util.LinkedList;42import java.util.List;43import java.util.ListIterator;44import java.util.PriorityQueue;45import java.util.Queue;46import java.util.Vector;47import java.util.concurrent.ArrayBlockingQueue;48import java.util.concurrent.BlockingDeque;49import java.util.concurrent.BlockingQueue;50import java.util.concurrent.ConcurrentLinkedDeque;51import java.util.concurrent.ConcurrentLinkedQueue;52import java.util.concurrent.CopyOnWriteArrayList;53import java.util.concurrent.CountDownLatch;54import java.util.concurrent.LinkedBlockingDeque;55import java.util.concurrent.LinkedBlockingQueue;56import java.util.concurrent.LinkedTransferQueue;57import java.util.concurrent.PriorityBlockingQueue;58import java.util.concurrent.ThreadLocalRandom;59import java.util.concurrent.TimeUnit;60import java.util.regex.Pattern;61import java.util.stream.Stream;6263/**64* Usage: [iterations=N] [size=N] [filter=REGEXP] [warmup=SECONDS]65*66* To run this in micro-benchmark mode, simply run as a normal java program.67* Be patient; this program runs for a very long time.68* For faster runs, restrict execution using command line args.69*70* @author Martin Buchholz71*/72public class RemoveMicroBenchmark {73abstract static class Job {74private final String name;75public Job(String name) { this.name = name; }76public String name() { return name; }77public abstract void work() throws Throwable;78public void run() {79try { work(); }80catch (Throwable ex) {81// current job cannot always be deduced from stacktrace.82throw new RuntimeException("Job failed: " + name(), ex);83}84}85}8687final int iterations;88final int size; // number of elements in collections89final double warmupSeconds;90final long warmupNanos;91final Pattern nameFilter; // select subset of Jobs to run92final boolean reverse; // reverse order of Jobs93final boolean shuffle; // randomize order of Jobs9495final ArrayList<Integer> elements; // contains size random Integers9697RemoveMicroBenchmark(String[] args) {98iterations = intArg(args, "iterations", 10_000);99size = intArg(args, "size", 1000);100warmupSeconds = doubleArg(args, "warmup", 7.0);101nameFilter = patternArg(args, "filter");102reverse = booleanArg(args, "reverse");103shuffle = booleanArg(args, "shuffle");104105warmupNanos = (long) (warmupSeconds * (1000L * 1000L * 1000L));106107elements = ThreadLocalRandom.current().ints(size)108.mapToObj(x -> x)109.collect(toCollection(ArrayList::new));110}111112// --------------- GC finalization infrastructure ---------------113114/** No guarantees, but effective in practice. */115static void forceFullGc() {116long timeoutMillis = 1000L;117CountDownLatch finalized = new CountDownLatch(1);118ReferenceQueue<Object> queue = new ReferenceQueue<>();119WeakReference<Object> ref = new WeakReference<>(120new Object() { protected void finalize() { finalized.countDown(); }},121queue);122try {123for (int tries = 3; tries--> 0; ) {124System.gc();125if (finalized.await(timeoutMillis, MILLISECONDS)126&& queue.remove(timeoutMillis) != null127&& ref.get() == null) {128System.runFinalization(); // try to pick up stragglers129return;130}131timeoutMillis *= 4;132}133} catch (InterruptedException unexpected) {134throw new AssertionError("unexpected InterruptedException");135}136throw new AssertionError("failed to do a \"full\" gc");137}138139/**140* Runs each job for long enough that all the runtime compilers141* have had plenty of time to warm up, i.e. get around to142* compiling everything worth compiling.143* Returns array of average times per job per run.144*/145long[] time0(List<Job> jobs) {146final int size = jobs.size();147long[] nanoss = new long[size];148for (int i = 0; i < size; i++) {149if (warmupNanos > 0) forceFullGc();150Job job = jobs.get(i);151long totalTime;152int runs = 0;153long startTime = System.nanoTime();154do { job.run(); runs++; }155while ((totalTime = System.nanoTime() - startTime) < warmupNanos);156nanoss[i] = totalTime/runs;157}158return nanoss;159}160161void time(List<Job> jobs) throws Throwable {162if (warmupNanos > 0) time0(jobs); // Warm up run163final int size = jobs.size();164final long[] nanoss = time0(jobs); // Real timing run165final long[] milliss = new long[size];166final double[] ratios = new double[size];167168final String nameHeader = "Method";169final String millisHeader = "Millis";170final String ratioHeader = "Ratio";171172int nameWidth = nameHeader.length();173int millisWidth = millisHeader.length();174int ratioWidth = ratioHeader.length();175176for (int i = 0; i < size; i++) {177nameWidth = Math.max(nameWidth, jobs.get(i).name().length());178179milliss[i] = nanoss[i]/(1000L * 1000L);180millisWidth = Math.max(millisWidth,181String.format("%d", milliss[i]).length());182183ratios[i] = (double) nanoss[i] / (double) nanoss[0];184ratioWidth = Math.max(ratioWidth,185String.format("%.3f", ratios[i]).length());186}187188String format = String.format("%%-%ds %%%dd %%%d.3f%%n",189nameWidth, millisWidth, ratioWidth);190String headerFormat = String.format("%%-%ds %%%ds %%%ds%%n",191nameWidth, millisWidth, ratioWidth);192System.out.printf(headerFormat, "Method", "Millis", "Ratio");193194// Print out absolute and relative times, calibrated against first job195for (int i = 0; i < size; i++)196System.out.printf(format, jobs.get(i).name(), milliss[i], ratios[i]);197}198199private static String keywordValue(String[] args, String keyword) {200for (String arg : args)201if (arg.startsWith(keyword))202return arg.substring(keyword.length() + 1);203return null;204}205206private static int intArg(String[] args, String keyword, int defaultValue) {207String val = keywordValue(args, keyword);208return (val == null) ? defaultValue : Integer.parseInt(val);209}210211private static double doubleArg(String[] args, String keyword, double defaultValue) {212String val = keywordValue(args, keyword);213return (val == null) ? defaultValue : Double.parseDouble(val);214}215216private static Pattern patternArg(String[] args, String keyword) {217String val = keywordValue(args, keyword);218return (val == null) ? null : Pattern.compile(val);219}220221private static boolean booleanArg(String[] args, String keyword) {222String val = keywordValue(args, keyword);223if (val == null || val.equals("false")) return false;224if (val.equals("true")) return true;225throw new IllegalArgumentException(val);226}227228private static void deoptimize(int sum) {229if (sum == 42)230System.out.println("the answer");231}232233private static <T> Iterable<T> backwards(final List<T> list) {234return new Iterable<T>() {235public Iterator<T> iterator() {236return new Iterator<T>() {237final ListIterator<T> it = list.listIterator(list.size());238public boolean hasNext() { return it.hasPrevious(); }239public T next() { return it.previous(); }240public void remove() { it.remove(); }};}};241}242243// Checks for correctness *and* prevents loop optimizations244static class Check {245private int sum;246public void sum(int sum) {247if (this.sum == 0)248this.sum = sum;249if (this.sum != sum)250throw new AssertionError("Sum mismatch");251}252}253volatile Check check = new Check();254255public static void main(String[] args) throws Throwable {256new RemoveMicroBenchmark(args).run();257}258259HashMap<Class<?>, String> goodClassName = new HashMap<>();260261String goodClassName(Class<?> klazz) {262return goodClassName.computeIfAbsent(263klazz,264k -> {265String simple = k.getSimpleName();266return (simple.equals("SubList")) // too simple!267? k.getName().replaceFirst(".*\\.", "")268: simple;269});270}271272String goodClassName(Object x) {273return goodClassName(x.getClass());274}275276static List<Integer> makeSubList(List<Integer> list) {277final ThreadLocalRandom rnd = ThreadLocalRandom.current();278int size = rnd.nextInt(4);279for (int i = size; i--> 0; )280list.add(rnd.nextInt());281int index = rnd.nextInt(size + 1);282return list.subList(index, index);283}284285private static <T> List<T> asSubList(List<T> list) {286return list.subList(0, list.size());287}288289@SafeVarargs @SuppressWarnings("varargs")290private static <T> Stream<T> concatStreams(Stream<T> ... streams) {291return Stream.of(streams).flatMap(s -> s);292}293294Class<?> topLevelClass(Object x) {295for (Class<?> k = x.getClass();; ) {296Class<?> enclosing = k.getEnclosingClass();297if (enclosing == null)298return k;299k = enclosing;300}301}302303void run() throws Throwable {304ArrayList<Job> jobs = Stream.<Collection<Integer>>of(305new ArrayList<>(),306makeSubList(new ArrayList<>()),307new LinkedList<>(),308makeSubList(new LinkedList<>()),309new Vector<>(),310makeSubList(new Vector<>()),311new CopyOnWriteArrayList<>(),312makeSubList(new CopyOnWriteArrayList<>()),313new ArrayDeque<>(),314new PriorityQueue<>(),315new ArrayBlockingQueue<>(elements.size()),316new ConcurrentLinkedQueue<>(),317new ConcurrentLinkedDeque<>(),318new LinkedBlockingQueue<>(),319new LinkedBlockingDeque<>(),320new LinkedTransferQueue<>(),321new PriorityBlockingQueue<>())322.flatMap(x -> jobs(x))323.filter(job ->324nameFilter == null || nameFilter.matcher(job.name()).find())325.collect(toCollection(ArrayList::new));326327if (reverse) Collections.reverse(jobs);328if (shuffle) Collections.shuffle(jobs);329330time(jobs);331}332333Stream<Job> jobs(Collection<Integer> x) {334return concatStreams(335collectionJobs(x),336337(CopyOnWriteArrayList.class.isAssignableFrom(topLevelClass(x)))338? Stream.empty()339: iteratorRemoveJobs(x),340341(x instanceof Queue)342? queueJobs((Queue<Integer>)x)343: Stream.empty(),344345(x instanceof Deque)346? dequeJobs((Deque<Integer>)x)347: Stream.empty(),348349(x instanceof BlockingQueue)350? blockingQueueJobs((BlockingQueue<Integer>)x)351: Stream.empty(),352353(x instanceof BlockingDeque)354? blockingDequeJobs((BlockingDeque<Integer>)x)355: Stream.empty());356}357358Collection<Integer> universeRecorder(int[] sum) {359return new ArrayList<>() {360public boolean contains(Object x) {361sum[0] += (Integer) x;362return true;363}};364}365366Collection<Integer> emptyRecorder(int[] sum) {367return new ArrayList<>() {368public boolean contains(Object x) {369sum[0] += (Integer) x;370return false;371}};372}373374Stream<Job> collectionJobs(Collection<Integer> x) {375final String klazz = goodClassName(x);376return Stream.of(377new Job(klazz + " removeIf") {378public void work() throws Throwable {379int[] sum = new int[1];380for (int i = 0; i < iterations; i++) {381sum[0] = 0;382x.addAll(elements);383x.removeIf(n -> { sum[0] += n; return true; });384check.sum(sum[0]);}}},385new Job(klazz + " removeIf rnd-two-pass") {386public void work() throws Throwable {387ThreadLocalRandom rnd = ThreadLocalRandom.current();388int[] sum = new int[1];389for (int i = 0; i < iterations; i++) {390sum[0] = 0;391x.addAll(elements);392x.removeIf(n -> {393boolean b = rnd.nextBoolean();394if (b) sum[0] += n;395return b; });396x.removeIf(n -> { sum[0] += n; return true; });397check.sum(sum[0]);}}},398new Job(klazz + " removeAll") {399public void work() throws Throwable {400int[] sum = new int[1];401Collection<Integer> universe = universeRecorder(sum);402for (int i = 0; i < iterations; i++) {403sum[0] = 0;404x.addAll(elements);405x.removeAll(universe);406check.sum(sum[0]);}}},407new Job(klazz + " retainAll") {408public void work() throws Throwable {409int[] sum = new int[1];410Collection<Integer> empty = emptyRecorder(sum);411for (int i = 0; i < iterations; i++) {412sum[0] = 0;413x.addAll(elements);414x.retainAll(empty);415check.sum(sum[0]);}}},416new Job(klazz + " clear") {417public void work() throws Throwable {418int[] sum = new int[1];419for (int i = 0; i < iterations; i++) {420sum[0] = 0;421x.addAll(elements);422x.forEach(e -> sum[0] += e);423x.clear();424check.sum(sum[0]);}}});425}426427Stream<Job> iteratorRemoveJobs(Collection<Integer> x) {428final String klazz = goodClassName(x);429return Stream.of(430new Job(klazz + " Iterator.remove") {431public void work() throws Throwable {432int[] sum = new int[1];433for (int i = 0; i < iterations; i++) {434sum[0] = 0;435x.addAll(elements);436Iterator<Integer> it = x.iterator();437while (it.hasNext()) {438sum[0] += it.next();439it.remove();440}441check.sum(sum[0]);}}},442new Job(klazz + " Iterator.remove-rnd-two-pass") {443public void work() throws Throwable {444ThreadLocalRandom rnd = ThreadLocalRandom.current();445int[] sum = new int[1];446for (int i = 0; i < iterations; i++) {447sum[0] = 0;448x.addAll(elements);449for (Iterator<Integer> it = x.iterator();450it.hasNext(); ) {451Integer e = it.next();452if (rnd.nextBoolean()) {453sum[0] += e;454it.remove();455}456}457for (Iterator<Integer> it = x.iterator();458it.hasNext(); ) {459sum[0] += it.next();460it.remove();461}462check.sum(sum[0]);}}});463}464465Stream<Job> queueJobs(Queue<Integer> x) {466final String klazz = goodClassName(x);467return Stream.of(468new Job(klazz + " poll()") {469public void work() throws Throwable {470int[] sum = new int[1];471for (int i = 0; i < iterations; i++) {472sum[0] = 0;473x.addAll(elements);474for (Integer e; (e = x.poll()) != null; )475sum[0] += e;476check.sum(sum[0]);}}});477}478479Stream<Job> dequeJobs(Deque<Integer> x) {480final String klazz = goodClassName(x);481return Stream.of(482new Job(klazz + " descendingIterator().remove") {483public void work() throws Throwable {484int[] sum = new int[1];485for (int i = 0; i < iterations; i++) {486sum[0] = 0;487x.addAll(elements);488Iterator<Integer> it = x.descendingIterator();489while (it.hasNext()) {490sum[0] += it.next();491it.remove();492}493check.sum(sum[0]);}}},494new Job(klazz + " pollFirst()") {495public void work() throws Throwable {496int[] sum = new int[1];497for (int i = 0; i < iterations; i++) {498sum[0] = 0;499x.addAll(elements);500for (Integer e; (e = x.pollFirst()) != null; )501sum[0] += e;502check.sum(sum[0]);}}},503new Job(klazz + " pollLast()") {504public void work() throws Throwable {505int[] sum = new int[1];506for (int i = 0; i < iterations; i++) {507sum[0] = 0;508x.addAll(elements);509for (Integer e; (e = x.pollLast()) != null; )510sum[0] += e;511check.sum(sum[0]);}}});512}513514Stream<Job> blockingQueueJobs(BlockingQueue<Integer> x) {515final String klazz = goodClassName(x);516return Stream.of(517new Job(klazz + " timed poll()") {518public void work() throws Throwable {519int[] sum = new int[1];520for (int i = 0; i < iterations; i++) {521sum[0] = 0;522x.addAll(elements);523for (Integer e; (e = x.poll(0L, TimeUnit.DAYS)) != null; )524sum[0] += e;525check.sum(sum[0]);}}},526new Job(klazz + " drainTo(sink)") {527public void work() throws Throwable {528ArrayList<Integer> sink = new ArrayList<>();529int[] sum = new int[1];530for (int i = 0; i < iterations; i++) {531sum[0] = 0;532sink.clear();533x.addAll(elements);534x.drainTo(sink);535sink.forEach(e -> sum[0] += e);536check.sum(sum[0]);}}},537new Job(klazz + " drainTo(sink, n)") {538public void work() throws Throwable {539ArrayList<Integer> sink = new ArrayList<>();540int[] sum = new int[1];541for (int i = 0; i < iterations; i++) {542sum[0] = 0;543sink.clear();544x.addAll(elements);545x.drainTo(sink, elements.size());546sink.forEach(e -> sum[0] += e);547check.sum(sum[0]);}}});548}549550Stream<Job> blockingDequeJobs(BlockingDeque<Integer> x) {551final String klazz = goodClassName(x);552return Stream.of(553new Job(klazz + " timed pollFirst()") {554public void work() throws Throwable {555int[] sum = new int[1];556for (int i = 0; i < iterations; i++) {557sum[0] = 0;558x.addAll(elements);559for (Integer e; (e = x.pollFirst(0L, TimeUnit.DAYS)) != null; )560sum[0] += e;561check.sum(sum[0]);}}},562new Job(klazz + " timed pollLast()") {563public void work() throws Throwable {564int[] sum = new int[1];565for (int i = 0; i < iterations; i++) {566sum[0] = 0;567x.addAll(elements);568for (Integer e; (e = x.pollLast(0L, TimeUnit.DAYS)) != null; )569sum[0] += e;570check.sum(sum[0]);}}});571}572}573574575