1/*
2 * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * This is not a regression test, but a micro-benchmark.
26 *
27 * I have run this as follows:
28 *
29 * repeat 5 for f in -client -server; do mergeBench dolphin . jr -dsa -da $f RangeCheckMicroBenchmark.java; done
30 *
31 *
32 * @author Martin Buchholz
33 */
34
35import java.util.*;
36import java.util.regex.Pattern;
37import java.util.concurrent.CountDownLatch;
38
39public class RangeCheckMicroBenchmark {
40    abstract static class Job {
41        private final String name;
42        Job(String name) { this.name = name; }
43        String name() { return name; }
44        abstract void work() throws Throwable;
45    }
46
47    private static void collectAllGarbage() {
48        final CountDownLatch drained = new CountDownLatch(1);
49        try {
50            System.gc();        // enqueue finalizable objects
51            new Object() { protected void finalize() {
52                drained.countDown(); }};
53            System.gc();        // enqueue detector
54            drained.await();    // wait for finalizer queue to drain
55            System.gc();        // cleanup finalized objects
56        } catch (InterruptedException e) { throw new Error(e); }
57    }
58
59    /**
60     * Runs each job for long enough that all the runtime compilers
61     * have had plenty of time to warm up, i.e. get around to
62     * compiling everything worth compiling.
63     * Returns array of average times per job per run.
64     */
65    private static long[] time0(Job ... jobs) throws Throwable {
66        final long warmupNanos = 10L * 1000L * 1000L * 1000L;
67        long[] nanoss = new long[jobs.length];
68        for (int i = 0; i < jobs.length; i++) {
69            collectAllGarbage();
70            long t0 = System.nanoTime();
71            long t;
72            int j = 0;
73            do { jobs[i].work(); j++; }
74            while ((t = System.nanoTime() - t0) < warmupNanos);
75            nanoss[i] = t/j;
76        }
77        return nanoss;
78    }
79
80    private static void time(Job ... jobs) throws Throwable {
81
82        long[] warmup = time0(jobs); // Warm up run
83        long[] nanoss = time0(jobs); // Real timing run
84        long[] milliss = new long[jobs.length];
85        double[] ratios = new double[jobs.length];
86
87        final String nameHeader   = "Method";
88        final String millisHeader = "Millis";
89        final String ratioHeader  = "Ratio";
90
91        int nameWidth   = nameHeader.length();
92        int millisWidth = millisHeader.length();
93        int ratioWidth  = ratioHeader.length();
94
95        for (int i = 0; i < jobs.length; i++) {
96            nameWidth = Math.max(nameWidth, jobs[i].name().length());
97
98            milliss[i] = nanoss[i]/(1000L * 1000L);
99            millisWidth = Math.max(millisWidth,
100                                   String.format("%d", milliss[i]).length());
101
102            ratios[i] = (double) nanoss[i] / (double) nanoss[0];
103            ratioWidth = Math.max(ratioWidth,
104                                  String.format("%.3f", ratios[i]).length());
105        }
106
107        String format = String.format("%%-%ds %%%dd %%%d.3f%%n",
108                                      nameWidth, millisWidth, ratioWidth);
109        String headerFormat = String.format("%%-%ds %%%ds %%%ds%%n",
110                                            nameWidth, millisWidth, ratioWidth);
111        System.out.printf(headerFormat, "Method", "Millis", "Ratio");
112
113        // Print out absolute and relative times, calibrated against first job
114        for (int i = 0; i < jobs.length; i++)
115            System.out.printf(format, jobs[i].name(), milliss[i], ratios[i]);
116    }
117
118    private static String keywordValue(String[] args, String keyword) {
119        for (String arg : args)
120            if (arg.startsWith(keyword))
121                return arg.substring(keyword.length() + 1);
122        return null;
123    }
124
125    private static int intArg(String[] args, String keyword, int defaultValue) {
126        String val = keywordValue(args, keyword);
127        return val == null ? defaultValue : Integer.parseInt(val);
128    }
129
130    private static Pattern patternArg(String[] args, String keyword) {
131        String val = keywordValue(args, keyword);
132        return val == null ? null : Pattern.compile(val);
133    }
134
135    private static Job[] filter(Pattern filter, Job[] jobs) {
136        if (filter == null) return jobs;
137        Job[] newJobs = new Job[jobs.length];
138        int n = 0;
139        for (Job job : jobs)
140            if (filter.matcher(job.name()).find())
141                newJobs[n++] = job;
142        // Arrays.copyOf not available in JDK 5
143        Job[] ret = new Job[n];
144        System.arraycopy(newJobs, 0, ret, 0, n);
145        return ret;
146    }
147
148    private static void deoptimize(ArrayList<Integer> list) {
149        for (Integer x : list)
150            if (x == null)
151                throw new Error();
152    }
153
154    /**
155     * Usage: [iterations=N] [size=N] [filter=REGEXP]
156     */
157    public static void main(String[] args) throws Throwable {
158        final int iterations = intArg(args, "iterations", 30000);
159        final int size       = intArg(args, "size", 1000);
160        final Pattern filter = patternArg(args, "filter");
161
162        final ArrayList<Integer> list = new ArrayList<>();
163        final Random rnd = new Random();
164        for (int i = 0; i < size; i++)
165            list.add(rnd.nextInt());
166
167        final Job[] jobs = {
168            new Job("get") { void work() {
169                for (int i = 0; i < iterations; i++) {
170                    for (int k = 0; k < size; k++)
171                        if (list.get(k) == 42)
172                            throw new Error();
173                }
174                deoptimize(list);}},
175            new Job("set") { void work() {
176                Integer[] xs = list.toArray(new Integer[size]);
177                for (int i = 0; i < iterations; i++) {
178                    for (int k = 0; k < size; k++)
179                        list.set(k, xs[k]);
180                }
181                deoptimize(list);}},
182            new Job("get/set") { void work() {
183                for (int i = 0; i < iterations; i++) {
184                    for (int k = 0; k < size; k++)
185                        list.set(k, list.get(size - k - 1));
186                }
187                deoptimize(list);}},
188            new Job("add/remove at end") { void work() {
189                Integer x = rnd.nextInt();
190                for (int i = 0; i < iterations; i++) {
191                    for (int k = 0; k < size - 1; k++) {
192                        list.add(size, x);
193                        list.remove(size);
194                    }
195                }
196                deoptimize(list);}},
197            new Job("subList get") { void work() {
198                List<Integer> sublist = list.subList(0, list.size());
199                for (int i = 0; i < iterations; i++) {
200                    for (int k = 0; k < size; k++)
201                        if (sublist.get(k) == 42)
202                            throw new Error();
203                }
204                deoptimize(list);}},
205            new Job("subList set") { void work() {
206                List<Integer> sublist = list.subList(0, list.size());
207                Integer[] xs = sublist.toArray(new Integer[size]);
208                for (int i = 0; i < iterations; i++) {
209                    for (int k = 0; k < size; k++)
210                        sublist.set(k, xs[k]);
211                }
212                deoptimize(list);}},
213            new Job("subList get/set") { void work() {
214                List<Integer> sublist = list.subList(0, list.size());
215                for (int i = 0; i < iterations; i++) {
216                    for (int k = 0; k < size; k++)
217                        sublist.set(k, sublist.get(size - k - 1));
218                }
219                deoptimize(list);}},
220            new Job("subList add/remove at end") { void work() {
221                List<Integer> sublist = list.subList(0, list.size());
222                Integer x = rnd.nextInt();
223                for (int i = 0; i < iterations; i++) {
224                    for (int k = 0; k < size - 1; k++) {
225                        sublist.add(size, x);
226                        sublist.remove(size);
227                    }
228                }
229                deoptimize(list);}}
230        };
231
232        time(filter(filter, jobs));
233    }
234}
235