Fun with Spliterators
Dr Heinz M. Kabutz
heinz at javaspecialists.eu
Thu Oct 31 20:59:10 UTC 2024
Hi Doug,
I've been looking at the spliterators in the LBQ and other concurrent
classes. I'm aware that the likelihood of anyone ever wanting to
parallel stream over one of these is extremely remote, so this is just a
curiosity.
In my experiment, I create 10m * hardware threads elements and put them
into various collections. I then parallel stream over them and count how
many times each thread executes is used.
Not surprisingly, the ArrayList does very well, since it is based on a
sized and subsized array.
Some of the other collections were a bit surprising. Since I'm busy
looking at the LBQ at the moment, here are its results:
LinkedBlockingQueue
ForkJoinPool.commonPool-worker-1=22242736 185.36%
ForkJoinPool.commonPool-worker-11=406 0.00%
ForkJoinPool.commonPool-worker-2=8169 0.07%
ForkJoinPool.commonPool-worker-3=23194669 193.29%
ForkJoinPool.commonPool-worker-4=22421660 186.85%
ForkJoinPool.commonPool-worker-5=79 0.00%
ForkJoinPool.commonPool-worker-6=24577826 204.82%
ForkJoinPool.commonPool-worker-7=27462223 228.85%
ForkJoinPool.commonPool-worker-9=92230 0.77%
main=2 0.00%
time = 985ms
We see that LBQ's parallel stream only used 10/12 cores and almost all
the work was done by 5 threads.
In contrast, ArrayList:
ArrayList
ForkJoinPool.commonPool-worker-1=11250000 112.50%
ForkJoinPool.commonPool-worker-10=9375000 93.75%
ForkJoinPool.commonPool-worker-11=11250000 112.50%
ForkJoinPool.commonPool-worker-2=7500000 75.00%
ForkJoinPool.commonPool-worker-3=9375000 93.75%
ForkJoinPool.commonPool-worker-4=9375000 93.75%
ForkJoinPool.commonPool-worker-5=11250000 112.50%
ForkJoinPool.commonPool-worker-6=11250000 112.50%
ForkJoinPool.commonPool-worker-7=9375000 93.75%
ForkJoinPool.commonPool-worker-8=7500000 75.00%
ForkJoinPool.commonPool-worker-9=11250000 112.50%
main=11250000 112.50%
time = 287ms
This is, of course, not surprising, given the linked vs array based
nature of the collections.
Here is my code for your amusement:
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.stream.*;
public class LinkedBlockingQueueSpliterating {
private static int ELEMENTS =
10_000_000 * Runtime.getRuntime().availableProcessors();
public static void main(String... args) {
test(new ArrayBlockingQueue<>(ELEMENTS));
test(new ArrayList<>());
test(new LinkedList<>());
test(new LinkedBlockingQueue<>());
test(new ConcurrentLinkedQueue<>());
test(new ConcurrentLinkedDeque<>());
test(new ArrayDeque<>());
}
private static void test(Collection<Integer> collection) {
System.out.println(collection.getClass().getSimpleName());
for (int i = 0; i < ELEMENTS; i++) {
collection.add(i);
}
long time = System.nanoTime();
try {
Map<String, Long> tasksPerThread =
collection.parallelStream()
.map(i -> Thread.currentThread().getName())
.collect(Collectors.groupingBy(
Function.identity(),
TreeMap::new,
Collectors.counting()
));
int expectedTasksPerThread = collection.size() /
tasksPerThread.size();
for (var entry : tasksPerThread.entrySet()) {
System.out.printf("%s %.2f%%%n", entry,
100.0 * entry.getValue() / expectedTasksPerThread);
}
} finally {
time = System.nanoTime() - time;
System.out.printf("time = %dms%n%n", (time / 1_000_000));
}
}
}
Regards
Heinz
--
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java™ Specialists' Newsletter" - www.javaspecialists.eu
Java Champion - www.javachampions.org
JavaOne Rock Star Speaker
Tel: +30 69 75 595 262
Skype: kabutz
More information about the concurrency-discuss
mailing list