ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/jtreg/util/Collection/IteratorMicroBenchmark.java
Revision: 1.37
Committed: Sun Mar 25 19:00:09 2018 UTC (6 years, 2 months ago) by jsr166
Branch: MAIN
Changes since 1.36: +0 -58 lines
Log Message:
delete experimental subListJob code

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