Skip to content

Commit a4f3a1d

Browse files
author
Doug Lea
committed
8200258: Improve CopyOnWriteArrayList subList code
Reviewed-by: martin, psandoz, smarks
1 parent f1e4c3c commit a4f3a1d

File tree

9 files changed

+1391
-771
lines changed

9 files changed

+1391
-771
lines changed

src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java

+321-193
Large diffs are not rendered by default.

test/jdk/java/util/Collection/IteratorMicroBenchmark.java

+104-56
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.Collection;
3737
import java.util.Collections;
3838
import java.util.Deque;
39+
import java.util.HashMap;
3940
import java.util.Iterator;
4041
import java.util.LinkedList;
4142
import java.util.List;
@@ -55,6 +56,7 @@
5556
import java.util.concurrent.ThreadLocalRandom;
5657
import java.util.concurrent.TimeUnit;
5758
import java.util.concurrent.atomic.LongAdder;
59+
import java.util.function.UnaryOperator;
5860
import java.util.regex.Pattern;
5961
import java.util.stream.Stream;
6062

@@ -75,6 +77,13 @@ abstract static class Job {
7577
public Job(String name) { this.name = name; }
7678
public String name() { return name; }
7779
public abstract void work() throws Throwable;
80+
public void run() {
81+
try { work(); }
82+
catch (Throwable ex) {
83+
// current job cannot always be deduced from stacktrace.
84+
throw new RuntimeException("Job failed: " + name(), ex);
85+
}
86+
}
7887
}
7988

8089
final int iterations;
@@ -102,6 +111,7 @@ abstract static class Job {
102111
static void forceFullGc() {
103112
CountDownLatch finalizeDone = new CountDownLatch(1);
104113
WeakReference<?> ref = new WeakReference<Object>(new Object() {
114+
@SuppressWarnings("deprecation")
105115
protected void finalize() { finalizeDone.countDown(); }});
106116
try {
107117
for (int i = 0; i < 10; i++) {
@@ -123,7 +133,7 @@ static void forceFullGc() {
123133
* compiling everything worth compiling.
124134
* Returns array of average times per job per run.
125135
*/
126-
long[] time0(List<Job> jobs) throws Throwable {
136+
long[] time0(List<Job> jobs) {
127137
final int size = jobs.size();
128138
long[] nanoss = new long[size];
129139
for (int i = 0; i < size; i++) {
@@ -132,7 +142,7 @@ long[] time0(List<Job> jobs) throws Throwable {
132142
long totalTime;
133143
int runs = 0;
134144
long startTime = System.nanoTime();
135-
do { job.work(); runs++; }
145+
do { job.run(); runs++; }
136146
while ((totalTime = System.nanoTime() - startTime) < warmupNanos);
137147
nanoss[i] = totalTime/runs;
138148
}
@@ -211,10 +221,6 @@ private static void deoptimize(int sum) {
211221
System.out.println("the answer");
212222
}
213223

214-
private static <T> List<T> asSubList(List<T> list) {
215-
return list.subList(0, list.size());
216-
}
217-
218224
private static <T> Iterable<T> backwards(final List<T> list) {
219225
return new Iterable<T>() {
220226
public Iterator<T> iterator() {
@@ -241,11 +247,32 @@ public static void main(String[] args) throws Throwable {
241247
new IteratorMicroBenchmark(args).run();
242248
}
243249

244-
void run() throws Throwable {
245-
// System.out.printf(
246-
// "iterations=%d size=%d, warmup=%1g, filter=\"%s\"%n",
247-
// iterations, size, warmupSeconds, nameFilter);
250+
HashMap<Class<?>, String> goodClassName = new HashMap<>();
251+
252+
String goodClassName(Class<?> klazz) {
253+
return goodClassName.computeIfAbsent(
254+
klazz,
255+
k -> {
256+
String simple = k.getSimpleName();
257+
return (simple.equals("SubList")) // too simple!
258+
? k.getName().replaceFirst(".*\\.", "")
259+
: simple;
260+
});
261+
}
248262

263+
static List<Integer> makeSubList(List<Integer> list) {
264+
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
265+
int size = list.size();
266+
if (size <= 2) return list.subList(0, size);
267+
List<Integer> subList = list.subList(rnd.nextInt(0, 2),
268+
size - rnd.nextInt(0, 2));
269+
List<Integer> copy = new ArrayList<>(list);
270+
subList.clear();
271+
subList.addAll(copy);
272+
return subList;
273+
}
274+
275+
void run() throws Throwable {
249276
final ArrayList<Integer> al = new ArrayList<>(size);
250277

251278
// Populate collections with random data
@@ -265,10 +292,14 @@ void run() throws Throwable {
265292

266293
ArrayList<Job> jobs = Stream.<Collection<Integer>>of(
267294
al, ad, abq,
295+
makeSubList(new ArrayList<>(al)),
268296
new LinkedList<>(al),
297+
makeSubList(new LinkedList<>(al)),
269298
new PriorityQueue<>(al),
270299
new Vector<>(al),
300+
makeSubList(new Vector<>(al)),
271301
new CopyOnWriteArrayList<>(al),
302+
makeSubList(new CopyOnWriteArrayList<>(al)),
272303
new ConcurrentLinkedQueue<>(al),
273304
new ConcurrentLinkedDeque<>(al),
274305
new LinkedBlockingQueue<>(al),
@@ -294,16 +325,25 @@ private <T> Stream<T> concatStreams(Stream<T> ... streams) {
294325
Stream<Job> jobs(Collection<Integer> x) {
295326
return concatStreams(
296327
collectionJobs(x),
328+
297329
(x instanceof Deque)
298330
? dequeJobs((Deque<Integer>)x)
299331
: Stream.empty(),
332+
300333
(x instanceof List)
301334
? listJobs((List<Integer>)x)
302335
: Stream.empty());
303336
}
304337

338+
Object sneakyAdder(int[] sneakySum) {
339+
return new Object() {
340+
public int hashCode() { throw new AssertionError(); }
341+
public boolean equals(Object z) {
342+
sneakySum[0] += (int) z; return false; }};
343+
}
344+
305345
Stream<Job> collectionJobs(Collection<Integer> x) {
306-
String klazz = x.getClass().getSimpleName();
346+
final String klazz = goodClassName(x.getClass());
307347
return Stream.of(
308348
new Job(klazz + " iterate for loop") {
309349
public void work() throws Throwable {
@@ -345,22 +385,28 @@ public void work() throws Throwable {
345385
new Job(klazz + " contains") {
346386
public void work() throws Throwable {
347387
int[] sum = new int[1];
348-
Object y = new Object() {
349-
public boolean equals(Object z) {
350-
sum[0] += (int) z; return false; }};
388+
Object sneakyAdder = sneakyAdder(sum);
389+
for (int i = 0; i < iterations; i++) {
390+
sum[0] = 0;
391+
if (x.contains(sneakyAdder)) throw new AssertionError();
392+
check.sum(sum[0]);}}},
393+
new Job(klazz + " containsAll") {
394+
public void work() throws Throwable {
395+
int[] sum = new int[1];
396+
Collection<Object> sneakyAdderCollection =
397+
Collections.singleton(sneakyAdder(sum));
351398
for (int i = 0; i < iterations; i++) {
352399
sum[0] = 0;
353-
if (x.contains(y)) throw new AssertionError();
400+
if (x.containsAll(sneakyAdderCollection))
401+
throw new AssertionError();
354402
check.sum(sum[0]);}}},
355403
new Job(klazz + " remove(Object)") {
356404
public void work() throws Throwable {
357405
int[] sum = new int[1];
358-
Object y = new Object() {
359-
public boolean equals(Object z) {
360-
sum[0] += (int) z; return false; }};
406+
Object sneakyAdder = sneakyAdder(sum);
361407
for (int i = 0; i < iterations; i++) {
362408
sum[0] = 0;
363-
if (x.remove(y)) throw new AssertionError();
409+
if (x.remove(sneakyAdder)) throw new AssertionError();
364410
check.sum(sum[0]);}}},
365411
new Job(klazz + " forEach") {
366412
public void work() throws Throwable {
@@ -446,7 +492,7 @@ public void work() throws Throwable {
446492
}
447493

448494
Stream<Job> dequeJobs(Deque<Integer> x) {
449-
String klazz = x.getClass().getSimpleName();
495+
String klazz = goodClassName(x.getClass());
450496
return Stream.of(
451497
new Job(klazz + " descendingIterator() loop") {
452498
public void work() throws Throwable {
@@ -466,48 +512,50 @@ public void work() throws Throwable {
466512
}
467513

468514
Stream<Job> listJobs(List<Integer> x) {
469-
String klazz = x.getClass().getSimpleName();
515+
final String klazz = goodClassName(x.getClass());
470516
return Stream.of(
471-
new Job(klazz + " subList toArray()") {
517+
new Job(klazz + " listIterator forward loop") {
518+
public void work() throws Throwable {
519+
for (int i = 0; i < iterations; i++) {
520+
int sum = 0;
521+
ListIterator<Integer> it = x.listIterator();
522+
while (it.hasNext())
523+
sum += it.next();
524+
check.sum(sum);}}},
525+
new Job(klazz + " listIterator backward loop") {
472526
public void work() throws Throwable {
473-
int size = x.size();
474527
for (int i = 0; i < iterations; i++) {
475-
int total = Stream.of(x.subList(0, size / 2),
476-
x.subList(size / 2, size))
477-
.mapToInt(subList -> {
478-
int sum = 0;
479-
for (Object o : subList.toArray())
480-
sum += (Integer) o;
481-
return sum; })
482-
.sum();
483-
check.sum(total);}}},
484-
new Job(klazz + " subList toArray(a)") {
528+
int sum = 0;
529+
ListIterator<Integer> it = x.listIterator(x.size());
530+
while (it.hasPrevious())
531+
sum += it.previous();
532+
check.sum(sum);}}},
533+
new Job(klazz + " indexOf") {
485534
public void work() throws Throwable {
486-
int size = x.size();
535+
int[] sum = new int[1];
536+
Object sneakyAdder = sneakyAdder(sum);
487537
for (int i = 0; i < iterations; i++) {
488-
int total = Stream.of(x.subList(0, size / 2),
489-
x.subList(size / 2, size))
490-
.mapToInt(subList -> {
491-
int sum = 0;
492-
Integer[] a = new Integer[subList.size()];
493-
for (Object o : subList.toArray(a))
494-
sum += (Integer) o;
495-
return sum; })
496-
.sum();
497-
check.sum(total);}}},
498-
new Job(klazz + " subList toArray(empty)") {
538+
sum[0] = 0;
539+
if (x.indexOf(sneakyAdder) != -1)
540+
throw new AssertionError();
541+
check.sum(sum[0]);}}},
542+
new Job(klazz + " lastIndexOf") {
499543
public void work() throws Throwable {
500-
int size = x.size();
501-
Integer[] empty = new Integer[0];
544+
int[] sum = new int[1];
545+
Object sneakyAdder = sneakyAdder(sum);
546+
for (int i = 0; i < iterations; i++) {
547+
sum[0] = 0;
548+
if (x.lastIndexOf(sneakyAdder) != -1)
549+
throw new AssertionError();
550+
check.sum(sum[0]);}}},
551+
new Job(klazz + " replaceAll") {
552+
public void work() throws Throwable {
553+
int[] sum = new int[1];
554+
UnaryOperator<Integer> sneakyAdder =
555+
x -> { sum[0] += x; return x; };
502556
for (int i = 0; i < iterations; i++) {
503-
int total = Stream.of(x.subList(0, size / 2),
504-
x.subList(size / 2, size))
505-
.mapToInt(subList -> {
506-
int sum = 0;
507-
for (Object o : subList.toArray(empty))
508-
sum += (Integer) o;
509-
return sum; })
510-
.sum();
511-
check.sum(total);}}});
557+
sum[0] = 0;
558+
x.replaceAll(sneakyAdder);
559+
check.sum(sum[0]);}}});
512560
}
513561
}

0 commit comments

Comments
 (0)