ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/jtreg/util/Collection/IteratorMicroBenchmark.java
Revision: 1.40
Committed: Mon Apr 2 20:32:53 2018 UTC (6 years, 2 months ago) by jsr166
Branch: MAIN
Changes since 1.39: +2 -5 lines
Log Message:
tidying

File Contents

# Content
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 * @test
26 * @summary micro-benchmark correctness mode
27 * @run main IteratorMicroBenchmark iterations=1 size=8 warmup=0
28 */
29
30 import static java.util.stream.Collectors.summingInt;
31 import static java.util.stream.Collectors.toCollection;
32
33 import java.lang.ref.WeakReference;
34 import java.util.ArrayDeque;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Deque;
39 import java.util.HashMap;
40 import java.util.Iterator;
41 import java.util.LinkedList;
42 import java.util.List;
43 import java.util.ListIterator;
44 import java.util.PriorityQueue;
45 import java.util.Spliterator;
46 import java.util.Vector;
47 import java.util.concurrent.ArrayBlockingQueue;
48 import java.util.concurrent.ConcurrentLinkedDeque;
49 import java.util.concurrent.ConcurrentLinkedQueue;
50 import java.util.concurrent.CopyOnWriteArrayList;
51 import java.util.concurrent.LinkedBlockingDeque;
52 import java.util.concurrent.LinkedBlockingQueue;
53 import java.util.concurrent.LinkedTransferQueue;
54 import java.util.concurrent.PriorityBlockingQueue;
55 import java.util.concurrent.CountDownLatch;
56 import java.util.concurrent.ThreadLocalRandom;
57 import java.util.concurrent.TimeUnit;
58 import java.util.concurrent.atomic.LongAdder;
59 import java.util.regex.Pattern;
60 import java.util.stream.Stream;
61
62 /**
63 * Usage: [iterations=N] [size=N] [filter=REGEXP] [warmup=SECONDS]
64 *
65 * To run this in micro-benchmark mode, simply run as a normal java program.
66 * Be patient; this program runs for a very long time.
67 * For faster runs, restrict execution using command line args.
68 *
69 * This is an interface based version of ArrayList/IteratorMicroBenchmark
70 *
71 * @author Martin Buchholz
72 */
73 public class IteratorMicroBenchmark {
74 abstract static class Job {
75 private final String name;
76 public Job(String name) { this.name = name; }
77 public String name() { return name; }
78 public abstract void work() throws Throwable;
79 public void run() {
80 try { work(); }
81 catch (Throwable ex) {
82 // current job cannot always be deduced from stacktrace.
83 throw new RuntimeException("Job failed: " + name(), ex);
84 }
85 }
86 }
87
88 final int iterations;
89 final int size; // number of elements in collections
90 final double warmupSeconds;
91 final long warmupNanos;
92 final Pattern nameFilter; // select subset of Jobs to run
93 final boolean reverse; // reverse order of Jobs
94 final boolean shuffle; // randomize order of Jobs
95
96 IteratorMicroBenchmark(String[] args) {
97 iterations = intArg(args, "iterations", 10_000);
98 size = intArg(args, "size", 1000);
99 warmupSeconds = doubleArg(args, "warmup", 7.0);
100 nameFilter = patternArg(args, "filter");
101 reverse = booleanArg(args, "reverse");
102 shuffle = booleanArg(args, "shuffle");
103
104 warmupNanos = (long) (warmupSeconds * (1000L * 1000L * 1000L));
105 }
106
107 // --------------- GC finalization infrastructure ---------------
108
109 /** No guarantees, but effective in practice. */
110 static void forceFullGc() {
111 CountDownLatch finalizeDone = new CountDownLatch(1);
112 WeakReference<?> ref = new WeakReference<Object>(new Object() {
113 @SuppressWarnings("deprecation")
114 protected void finalize() { finalizeDone.countDown(); }});
115 try {
116 for (int i = 0; i < 10; i++) {
117 System.gc();
118 if (finalizeDone.await(1L, TimeUnit.SECONDS) && ref.get() == null) {
119 System.runFinalization(); // try to pick up stragglers
120 return;
121 }
122 }
123 } catch (InterruptedException unexpected) {
124 throw new AssertionError("unexpected InterruptedException");
125 }
126 throw new AssertionError("failed to do a \"full\" gc");
127 }
128
129 /**
130 * Runs each job for long enough that all the runtime compilers
131 * have had plenty of time to warm up, i.e. get around to
132 * compiling everything worth compiling.
133 * Returns array of average times per job per run.
134 */
135 long[] time0(List<Job> jobs) {
136 final int size = jobs.size();
137 long[] nanoss = new long[size];
138 for (int i = 0; i < size; i++) {
139 if (warmupNanos > 0) forceFullGc();
140 Job job = jobs.get(i);
141 long totalTime;
142 int runs = 0;
143 long startTime = System.nanoTime();
144 do { job.run(); runs++; }
145 while ((totalTime = System.nanoTime() - startTime) < warmupNanos);
146 nanoss[i] = totalTime/runs;
147 }
148 return nanoss;
149 }
150
151 void time(List<Job> jobs) throws Throwable {
152 if (warmupNanos > 0) time0(jobs); // Warm up run
153 final int size = jobs.size();
154 final long[] nanoss = time0(jobs); // Real timing run
155 final long[] milliss = new long[size];
156 final double[] ratios = new double[size];
157
158 final String nameHeader = "Method";
159 final String millisHeader = "Millis";
160 final String ratioHeader = "Ratio";
161
162 int nameWidth = nameHeader.length();
163 int millisWidth = millisHeader.length();
164 int ratioWidth = ratioHeader.length();
165
166 for (int i = 0; i < size; i++) {
167 nameWidth = Math.max(nameWidth, jobs.get(i).name().length());
168
169 milliss[i] = nanoss[i]/(1000L * 1000L);
170 millisWidth = Math.max(millisWidth,
171 String.format("%d", milliss[i]).length());
172
173 ratios[i] = (double) nanoss[i] / (double) nanoss[0];
174 ratioWidth = Math.max(ratioWidth,
175 String.format("%.3f", ratios[i]).length());
176 }
177
178 String format = String.format("%%-%ds %%%dd %%%d.3f%%n",
179 nameWidth, millisWidth, ratioWidth);
180 String headerFormat = String.format("%%-%ds %%%ds %%%ds%%n",
181 nameWidth, millisWidth, ratioWidth);
182 System.out.printf(headerFormat, "Method", "Millis", "Ratio");
183
184 // Print out absolute and relative times, calibrated against first job
185 for (int i = 0; i < size; i++)
186 System.out.printf(format, jobs.get(i).name(), milliss[i], ratios[i]);
187 }
188
189 private static String keywordValue(String[] args, String keyword) {
190 for (String arg : args)
191 if (arg.startsWith(keyword))
192 return arg.substring(keyword.length() + 1);
193 return null;
194 }
195
196 private static int intArg(String[] args, String keyword, int defaultValue) {
197 String val = keywordValue(args, keyword);
198 return (val == null) ? defaultValue : Integer.parseInt(val);
199 }
200
201 private static double doubleArg(String[] args, String keyword, double defaultValue) {
202 String val = keywordValue(args, keyword);
203 return (val == null) ? defaultValue : Double.parseDouble(val);
204 }
205
206 private static Pattern patternArg(String[] args, String keyword) {
207 String val = keywordValue(args, keyword);
208 return (val == null) ? null : Pattern.compile(val);
209 }
210
211 private static boolean booleanArg(String[] args, String keyword) {
212 String val = keywordValue(args, keyword);
213 if (val == null || val.equals("false")) return false;
214 if (val.equals("true")) return true;
215 throw new IllegalArgumentException(val);
216 }
217
218 private static void deoptimize(int sum) {
219 if (sum == 42)
220 System.out.println("the answer");
221 }
222
223 private static <T> Iterable<T> backwards(final List<T> list) {
224 return new Iterable<T>() {
225 public Iterator<T> iterator() {
226 return new Iterator<T>() {
227 final ListIterator<T> it = list.listIterator(list.size());
228 public boolean hasNext() { return it.hasPrevious(); }
229 public T next() { return it.previous(); }
230 public void remove() { it.remove(); }};}};
231 }
232
233 // Checks for correctness *and* prevents loop optimizations
234 static class Check {
235 private int sum;
236 public void sum(int sum) {
237 if (this.sum == 0)
238 this.sum = sum;
239 if (this.sum != sum)
240 throw new AssertionError("Sum mismatch");
241 }
242 }
243 volatile Check check = new Check();
244
245 public static void main(String[] args) throws Throwable {
246 new IteratorMicroBenchmark(args).run();
247 }
248
249 HashMap<Class<?>, String> goodClassName = new HashMap<>();
250
251 String goodClassName(Class<?> klazz) {
252 return goodClassName.computeIfAbsent(
253 klazz,
254 k -> {
255 String simple = k.getSimpleName();
256 return (simple.equals("SubList")) // too simple!
257 ? k.getName().replaceFirst(".*\\.", "")
258 : simple;
259 });
260 }
261
262 static List<Integer> makeSubList(List<Integer> list) {
263 final ThreadLocalRandom rnd = ThreadLocalRandom.current();
264 int size = list.size();
265 if (size <= 2) return list.subList(0, size);
266 List<Integer> subList = list.subList(rnd.nextInt(0, 2),
267 size - rnd.nextInt(0, 2));
268 List<Integer> copy = new ArrayList<>(list);
269 subList.clear();
270 subList.addAll(copy);
271 return subList;
272 }
273
274 void run() throws Throwable {
275 final ArrayList<Integer> al = new ArrayList<>(size);
276
277 // Populate collections with random data
278 final ThreadLocalRandom rnd = ThreadLocalRandom.current();
279 for (int i = 0; i < size; i++)
280 al.add(rnd.nextInt(size));
281
282 final ArrayDeque<Integer> ad = new ArrayDeque<>(al);
283 final ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<>(al.size());
284 abq.addAll(al);
285
286 // shuffle circular array elements so they wrap
287 for (int i = 0, n = rnd.nextInt(size); i < n; i++) {
288 ad.addLast(ad.removeFirst());
289 abq.add(abq.remove());
290 }
291
292 ArrayList<Job> jobs = Stream.<Collection<Integer>>of(
293 al, ad, abq,
294 makeSubList(new ArrayList<>(al)),
295 new LinkedList<>(al),
296 makeSubList(new LinkedList<>(al)),
297 new PriorityQueue<>(al),
298 new Vector<>(al),
299 makeSubList(new Vector<>(al)),
300 new CopyOnWriteArrayList<>(al),
301 makeSubList(new CopyOnWriteArrayList<>(al)),
302 new ConcurrentLinkedQueue<>(al),
303 new ConcurrentLinkedDeque<>(al),
304 new LinkedBlockingQueue<>(al),
305 new LinkedBlockingDeque<>(al),
306 new LinkedTransferQueue<>(al),
307 new PriorityBlockingQueue<>(al))
308 .flatMap(x -> jobs(x))
309 .filter(job ->
310 nameFilter == null || nameFilter.matcher(job.name()).find())
311 .collect(toCollection(ArrayList::new));
312
313 if (reverse) Collections.reverse(jobs);
314 if (shuffle) Collections.shuffle(jobs);
315
316 time(jobs);
317 }
318
319 @SafeVarargs @SuppressWarnings("varargs")
320 private <T> Stream<T> concatStreams(Stream<T> ... streams) {
321 return Stream.of(streams).flatMap(s -> s);
322 }
323
324 Stream<Job> jobs(Collection<Integer> x) {
325 return concatStreams(
326 collectionJobs(x),
327
328 (x instanceof Deque)
329 ? dequeJobs((Deque<Integer>)x)
330 : Stream.empty(),
331
332 (x instanceof List)
333 ? listJobs((List<Integer>)x)
334 : Stream.empty());
335 }
336
337 Object sneakyAdder(int[] sneakySum) {
338 return new Object() {
339 public int hashCode() { throw new AssertionError(); }
340 public boolean equals(Object z) {
341 sneakySum[0] += (int) z; return false; }};
342 }
343
344 Stream<Job> collectionJobs(Collection<Integer> x) {
345 final String klazz = goodClassName(x.getClass());
346 return Stream.of(
347 new Job(klazz + " iterate for loop") {
348 public void work() throws Throwable {
349 for (int i = 0; i < iterations; i++) {
350 int sum = 0;
351 for (Integer n : x)
352 sum += n;
353 check.sum(sum);}}},
354 new Job(klazz + " iterator().forEachRemaining()") {
355 public void work() throws Throwable {
356 int[] sum = new int[1];
357 for (int i = 0; i < iterations; i++) {
358 sum[0] = 0;
359 x.iterator().forEachRemaining(n -> sum[0] += n);
360 check.sum(sum[0]);}}},
361 new Job(klazz + " spliterator().tryAdvance()") {
362 public void work() throws Throwable {
363 int[] sum = new int[1];
364 for (int i = 0; i < iterations; i++) {
365 sum[0] = 0;
366 Spliterator<Integer> spliterator = x.spliterator();
367 do {} while (spliterator.tryAdvance(n -> sum[0] += n));
368 check.sum(sum[0]);}}},
369 new Job(klazz + " spliterator().forEachRemaining()") {
370 public void work() throws Throwable {
371 int[] sum = new int[1];
372 for (int i = 0; i < iterations; i++) {
373 sum[0] = 0;
374 x.spliterator().forEachRemaining(n -> sum[0] += n);
375 check.sum(sum[0]);}}},
376 new Job(klazz + " removeIf") {
377 public void work() throws Throwable {
378 int[] sum = new int[1];
379 for (int i = 0; i < iterations; i++) {
380 sum[0] = 0;
381 if (x.removeIf(n -> { sum[0] += n; return false; }))
382 throw new AssertionError();
383 check.sum(sum[0]);}}},
384 new Job(klazz + " contains") {
385 public void work() throws Throwable {
386 int[] sum = new int[1];
387 Object sneakyAdder = sneakyAdder(sum);
388 for (int i = 0; i < iterations; i++) {
389 sum[0] = 0;
390 if (x.contains(sneakyAdder)) throw new AssertionError();
391 check.sum(sum[0]);}}},
392 new Job(klazz + " remove(Object)") {
393 public void work() throws Throwable {
394 int[] sum = new int[1];
395 Object sneakyAdder = sneakyAdder(sum);
396 for (int i = 0; i < iterations; i++) {
397 sum[0] = 0;
398 if (x.remove(sneakyAdder)) throw new AssertionError();
399 check.sum(sum[0]);}}},
400 new Job(klazz + " forEach") {
401 public void work() throws Throwable {
402 int[] sum = new int[1];
403 for (int i = 0; i < iterations; i++) {
404 sum[0] = 0;
405 x.forEach(n -> sum[0] += n);
406 check.sum(sum[0]);}}},
407 new Job(klazz + " toArray()") {
408 public void work() throws Throwable {
409 int[] sum = new int[1];
410 for (int i = 0; i < iterations; i++) {
411 sum[0] = 0;
412 for (Object o : x.toArray())
413 sum[0] += (Integer) o;
414 check.sum(sum[0]);}}},
415 new Job(klazz + " toArray(a)") {
416 public void work() throws Throwable {
417 Integer[] a = new Integer[x.size()];
418 int[] sum = new int[1];
419 for (int i = 0; i < iterations; i++) {
420 sum[0] = 0;
421 x.toArray(a);
422 for (Object o : a)
423 sum[0] += (Integer) o;
424 check.sum(sum[0]);}}},
425 new Job(klazz + " toArray(empty)") {
426 public void work() throws Throwable {
427 Integer[] empty = new Integer[0];
428 int[] sum = new int[1];
429 for (int i = 0; i < iterations; i++) {
430 sum[0] = 0;
431 for (Integer o : x.toArray(empty))
432 sum[0] += o;
433 check.sum(sum[0]);}}},
434 new Job(klazz + " stream().forEach") {
435 public void work() throws Throwable {
436 int[] sum = new int[1];
437 for (int i = 0; i < iterations; i++) {
438 sum[0] = 0;
439 x.stream().forEach(n -> sum[0] += n);
440 check.sum(sum[0]);}}},
441 new Job(klazz + " stream().mapToInt") {
442 public void work() throws Throwable {
443 for (int i = 0; i < iterations; i++) {
444 check.sum(x.stream().mapToInt(e -> e).sum());}}},
445 new Job(klazz + " stream().collect") {
446 public void work() throws Throwable {
447 for (int i = 0; i < iterations; i++) {
448 check.sum(x.stream()
449 .collect(summingInt(e -> e)));}}},
450 new Job(klazz + " stream()::iterator") {
451 public void work() throws Throwable {
452 int[] sum = new int[1];
453 for (int i = 0; i < iterations; i++) {
454 sum[0] = 0;
455 for (Integer o : (Iterable<Integer>) x.stream()::iterator)
456 sum[0] += o;
457 check.sum(sum[0]);}}},
458 new Job(klazz + " parallelStream().forEach") {
459 public void work() throws Throwable {
460 for (int i = 0; i < iterations; i++) {
461 LongAdder sum = new LongAdder();
462 x.parallelStream().forEach(n -> sum.add(n));
463 check.sum((int) sum.sum());}}},
464 new Job(klazz + " parallelStream().mapToInt") {
465 public void work() throws Throwable {
466 for (int i = 0; i < iterations; i++) {
467 check.sum(x.parallelStream().mapToInt(e -> e).sum());}}},
468 new Job(klazz + " parallelStream().collect") {
469 public void work() throws Throwable {
470 for (int i = 0; i < iterations; i++) {
471 check.sum(x.parallelStream()
472 .collect(summingInt(e -> e)));}}},
473 new Job(klazz + " parallelStream()::iterator") {
474 public void work() throws Throwable {
475 int[] sum = new int[1];
476 for (int i = 0; i < iterations; i++) {
477 sum[0] = 0;
478 for (Integer o : (Iterable<Integer>) x.parallelStream()::iterator)
479 sum[0] += o;
480 check.sum(sum[0]);}}});
481 }
482
483 Stream<Job> dequeJobs(Deque<Integer> x) {
484 String klazz = goodClassName(x.getClass());
485 return Stream.of(
486 new Job(klazz + " descendingIterator() loop") {
487 public void work() throws Throwable {
488 for (int i = 0; i < iterations; i++) {
489 int sum = 0;
490 Iterator<Integer> it = x.descendingIterator();
491 while (it.hasNext())
492 sum += it.next();
493 check.sum(sum);}}},
494 new Job(klazz + " descendingIterator().forEachRemaining()") {
495 public void work() throws Throwable {
496 int[] sum = new int[1];
497 for (int i = 0; i < iterations; i++) {
498 sum[0] = 0;
499 x.descendingIterator().forEachRemaining(n -> sum[0] += n);
500 check.sum(sum[0]);}}});
501 }
502
503 Stream<Job> listJobs(List<Integer> x) {
504 final String klazz = goodClassName(x.getClass());
505 return Stream.of(
506 new Job(klazz + " indexOf") {
507 public void work() throws Throwable {
508 int[] sum = new int[1];
509 Object sneakyAdder = sneakyAdder(sum);
510 for (int i = 0; i < iterations; i++) {
511 sum[0] = 0;
512 if (x.indexOf(sneakyAdder) != -1)
513 throw new AssertionError();
514 check.sum(sum[0]);}}},
515 new Job(klazz + " lastIndexOf") {
516 public void work() throws Throwable {
517 int[] sum = new int[1];
518 Object sneakyAdder = sneakyAdder(sum);
519 for (int i = 0; i < iterations; i++) {
520 sum[0] = 0;
521 if (x.lastIndexOf(sneakyAdder) != -1)
522 throw new AssertionError();
523 check.sum(sum[0]);}}});
524 }
525 }