mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8196207: Inefficient ArrayList.subList().toArray()
Reviewed-by: martin, psandoz, jrose, redestad
This commit is contained in:
parent
35a7cc10b7
commit
bbb4bcd69d
3 changed files with 124 additions and 34 deletions
|
@ -1143,6 +1143,23 @@ public class ArrayList<E> extends AbstractList<E>
|
||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object[] toArray() {
|
||||||
|
checkForComodification();
|
||||||
|
return Arrays.copyOfRange(root.elementData, offset, offset + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T[] toArray(T[] a) {
|
||||||
|
checkForComodification();
|
||||||
|
if (a.length < size)
|
||||||
|
return (T[]) Arrays.copyOfRange(
|
||||||
|
root.elementData, offset, offset + size, a.getClass());
|
||||||
|
System.arraycopy(root.elementData, offset, a, 0, size);
|
||||||
|
if (a.length > size)
|
||||||
|
a[size] = null;
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
public Iterator<E> iterator() {
|
public Iterator<E> iterator() {
|
||||||
return listIterator();
|
return listIterator();
|
||||||
}
|
}
|
||||||
|
|
|
@ -642,6 +642,24 @@ public class IteratorMicroBenchmark {
|
||||||
for (Object o : a)
|
for (Object o : a)
|
||||||
sum[0] += (Integer) o;
|
sum[0] += (Integer) o;
|
||||||
check.sum(sum[0]);}}},
|
check.sum(sum[0]);}}},
|
||||||
|
new Job("ArrayList subList .toArray()") {
|
||||||
|
public void work() throws Throwable {
|
||||||
|
int[] sum = new int[1];
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
sum[0] = 0;
|
||||||
|
for (Object o : asSubList(al).toArray())
|
||||||
|
sum[0] += (Integer) o;
|
||||||
|
check.sum(sum[0]);}}},
|
||||||
|
new Job("ArrayList subList .toArray(a)") {
|
||||||
|
public void work() throws Throwable {
|
||||||
|
Integer[] a = new Integer[size];
|
||||||
|
int[] sum = new int[1];
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
sum[0] = 0;
|
||||||
|
asSubList(al).toArray(a);
|
||||||
|
for (Object o : a)
|
||||||
|
sum[0] += (Integer) o;
|
||||||
|
check.sum(sum[0]);}}},
|
||||||
new Job("ArrayDeque.toArray()") {
|
new Job("ArrayDeque.toArray()") {
|
||||||
public void work() throws Throwable {
|
public void work() throws Throwable {
|
||||||
int[] sum = new int[1];
|
int[] sum = new int[1];
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static java.util.stream.Collectors.summingInt;
|
import static java.util.stream.Collectors.summingInt;
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toCollection;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
|
@ -46,6 +46,7 @@ import java.util.Vector;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.LinkedBlockingDeque;
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.LinkedTransferQueue;
|
import java.util.concurrent.LinkedTransferQueue;
|
||||||
|
@ -55,6 +56,7 @@ import java.util.concurrent.ThreadLocalRandom;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.LongAdder;
|
import java.util.concurrent.atomic.LongAdder;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Usage: [iterations=N] [size=N] [filter=REGEXP] [warmup=SECONDS]
|
* Usage: [iterations=N] [size=N] [filter=REGEXP] [warmup=SECONDS]
|
||||||
|
@ -79,7 +81,7 @@ public class IteratorMicroBenchmark {
|
||||||
final int size; // number of elements in collections
|
final int size; // number of elements in collections
|
||||||
final double warmupSeconds;
|
final double warmupSeconds;
|
||||||
final long warmupNanos;
|
final long warmupNanos;
|
||||||
final Pattern filter; // select subset of Jobs to run
|
final Pattern nameFilter; // select subset of Jobs to run
|
||||||
final boolean reverse; // reverse order of Jobs
|
final boolean reverse; // reverse order of Jobs
|
||||||
final boolean shuffle; // randomize order of Jobs
|
final boolean shuffle; // randomize order of Jobs
|
||||||
|
|
||||||
|
@ -87,7 +89,7 @@ public class IteratorMicroBenchmark {
|
||||||
iterations = intArg(args, "iterations", 10_000);
|
iterations = intArg(args, "iterations", 10_000);
|
||||||
size = intArg(args, "size", 1000);
|
size = intArg(args, "size", 1000);
|
||||||
warmupSeconds = doubleArg(args, "warmup", 7.0);
|
warmupSeconds = doubleArg(args, "warmup", 7.0);
|
||||||
filter = patternArg(args, "filter");
|
nameFilter = patternArg(args, "filter");
|
||||||
reverse = booleanArg(args, "reverse");
|
reverse = booleanArg(args, "reverse");
|
||||||
shuffle = booleanArg(args, "shuffle");
|
shuffle = booleanArg(args, "shuffle");
|
||||||
|
|
||||||
|
@ -204,13 +206,6 @@ public class IteratorMicroBenchmark {
|
||||||
throw new IllegalArgumentException(val);
|
throw new IllegalArgumentException(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Job> filter(Pattern filter, List<Job> jobs) {
|
|
||||||
return (filter == null) ? jobs
|
|
||||||
: jobs.stream()
|
|
||||||
.filter(job -> filter.matcher(job.name()).find())
|
|
||||||
.collect(toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void deoptimize(int sum) {
|
private static void deoptimize(int sum) {
|
||||||
if (sum == 42)
|
if (sum == 42)
|
||||||
System.out.println("the answer");
|
System.out.println("the answer");
|
||||||
|
@ -249,7 +244,7 @@ public class IteratorMicroBenchmark {
|
||||||
void run() throws Throwable {
|
void run() throws Throwable {
|
||||||
// System.out.printf(
|
// System.out.printf(
|
||||||
// "iterations=%d size=%d, warmup=%1g, filter=\"%s\"%n",
|
// "iterations=%d size=%d, warmup=%1g, filter=\"%s\"%n",
|
||||||
// iterations, size, warmupSeconds, filter);
|
// iterations, size, warmupSeconds, nameFilter);
|
||||||
|
|
||||||
final ArrayList<Integer> al = new ArrayList<>(size);
|
final ArrayList<Integer> al = new ArrayList<>(size);
|
||||||
|
|
||||||
|
@ -268,34 +263,48 @@ public class IteratorMicroBenchmark {
|
||||||
abq.add(abq.remove());
|
abq.add(abq.remove());
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Job> jobs = new ArrayList<>();
|
ArrayList<Job> jobs = Stream.<Collection<Integer>>of(
|
||||||
|
al, ad, abq,
|
||||||
List.<Collection<Integer>>of(
|
new LinkedList<>(al),
|
||||||
al, ad, abq,
|
new PriorityQueue<>(al),
|
||||||
new LinkedList<>(al),
|
new Vector<>(al),
|
||||||
new PriorityQueue<>(al),
|
new CopyOnWriteArrayList<>(al),
|
||||||
new Vector<>(al),
|
new ConcurrentLinkedQueue<>(al),
|
||||||
new ConcurrentLinkedQueue<>(al),
|
new ConcurrentLinkedDeque<>(al),
|
||||||
new ConcurrentLinkedDeque<>(al),
|
new LinkedBlockingQueue<>(al),
|
||||||
new LinkedBlockingQueue<>(al),
|
new LinkedBlockingDeque<>(al),
|
||||||
new LinkedBlockingDeque<>(al),
|
new LinkedTransferQueue<>(al),
|
||||||
new LinkedTransferQueue<>(al),
|
new PriorityBlockingQueue<>(al))
|
||||||
new PriorityBlockingQueue<>(al)).forEach(
|
.flatMap(x -> jobs(x))
|
||||||
x -> {
|
.filter(job ->
|
||||||
jobs.addAll(collectionJobs(x));
|
nameFilter == null || nameFilter.matcher(job.name()).find())
|
||||||
if (x instanceof Deque)
|
.collect(toCollection(ArrayList::new));
|
||||||
jobs.addAll(dequeJobs((Deque<Integer>)x));
|
|
||||||
});
|
|
||||||
|
|
||||||
if (reverse) Collections.reverse(jobs);
|
if (reverse) Collections.reverse(jobs);
|
||||||
if (shuffle) Collections.shuffle(jobs);
|
if (shuffle) Collections.shuffle(jobs);
|
||||||
|
|
||||||
time(filter(filter, jobs));
|
time(jobs);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Job> collectionJobs(Collection<Integer> x) {
|
@SafeVarargs @SuppressWarnings("varargs")
|
||||||
|
private <T> Stream<T> concatStreams(Stream<T> ... streams) {
|
||||||
|
return Stream.of(streams).flatMap(s -> s);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<Job> jobs(Collection<Integer> x) {
|
||||||
|
return concatStreams(
|
||||||
|
collectionJobs(x),
|
||||||
|
(x instanceof Deque)
|
||||||
|
? dequeJobs((Deque<Integer>)x)
|
||||||
|
: Stream.empty(),
|
||||||
|
(x instanceof List)
|
||||||
|
? listJobs((List<Integer>)x)
|
||||||
|
: Stream.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream<Job> collectionJobs(Collection<Integer> x) {
|
||||||
String klazz = x.getClass().getSimpleName();
|
String klazz = x.getClass().getSimpleName();
|
||||||
return List.of(
|
return Stream.of(
|
||||||
new Job(klazz + " iterate for loop") {
|
new Job(klazz + " iterate for loop") {
|
||||||
public void work() throws Throwable {
|
public void work() throws Throwable {
|
||||||
for (int i = 0; i < iterations; i++) {
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
@ -436,9 +445,9 @@ public class IteratorMicroBenchmark {
|
||||||
check.sum(sum[0]);}}});
|
check.sum(sum[0]);}}});
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Job> dequeJobs(Deque<Integer> x) {
|
Stream<Job> dequeJobs(Deque<Integer> x) {
|
||||||
String klazz = x.getClass().getSimpleName();
|
String klazz = x.getClass().getSimpleName();
|
||||||
return List.of(
|
return Stream.of(
|
||||||
new Job(klazz + " descendingIterator() loop") {
|
new Job(klazz + " descendingIterator() loop") {
|
||||||
public void work() throws Throwable {
|
public void work() throws Throwable {
|
||||||
for (int i = 0; i < iterations; i++) {
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
@ -455,4 +464,50 @@ public class IteratorMicroBenchmark {
|
||||||
x.descendingIterator().forEachRemaining(n -> sum[0] += n);
|
x.descendingIterator().forEachRemaining(n -> sum[0] += n);
|
||||||
check.sum(sum[0]);}}});
|
check.sum(sum[0]);}}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<Job> listJobs(List<Integer> x) {
|
||||||
|
String klazz = x.getClass().getSimpleName();
|
||||||
|
return Stream.of(
|
||||||
|
new Job(klazz + " subList toArray()") {
|
||||||
|
public void work() throws Throwable {
|
||||||
|
int size = x.size();
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
int total = Stream.of(x.subList(0, size / 2),
|
||||||
|
x.subList(size / 2, size))
|
||||||
|
.mapToInt(subList -> {
|
||||||
|
int sum = 0;
|
||||||
|
for (Object o : subList.toArray())
|
||||||
|
sum += (Integer) o;
|
||||||
|
return sum; })
|
||||||
|
.sum();
|
||||||
|
check.sum(total);}}},
|
||||||
|
new Job(klazz + " subList toArray(a)") {
|
||||||
|
public void work() throws Throwable {
|
||||||
|
int size = x.size();
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
int total = Stream.of(x.subList(0, size / 2),
|
||||||
|
x.subList(size / 2, size))
|
||||||
|
.mapToInt(subList -> {
|
||||||
|
int sum = 0;
|
||||||
|
Integer[] a = new Integer[subList.size()];
|
||||||
|
for (Object o : subList.toArray(a))
|
||||||
|
sum += (Integer) o;
|
||||||
|
return sum; })
|
||||||
|
.sum();
|
||||||
|
check.sum(total);}}},
|
||||||
|
new Job(klazz + " subList toArray(empty)") {
|
||||||
|
public void work() throws Throwable {
|
||||||
|
int size = x.size();
|
||||||
|
Integer[] empty = new Integer[0];
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
int total = Stream.of(x.subList(0, size / 2),
|
||||||
|
x.subList(size / 2, size))
|
||||||
|
.mapToInt(subList -> {
|
||||||
|
int sum = 0;
|
||||||
|
for (Object o : subList.toArray(empty))
|
||||||
|
sum += (Integer) o;
|
||||||
|
return sum; })
|
||||||
|
.sum();
|
||||||
|
check.sum(total);}}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue