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