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