/* __ __ __ __ __ ___ * \ \ / / \ \ / / __/ * \ \/ / /\ \ \/ / / * \____/__/ \__\____/__/.ɪᴏ * ᶜᵒᵖʸʳᶦᵍʰᵗ ᵇʸ ᵛᵃᵛʳ ⁻ ˡᶦᶜᵉⁿˢᵉᵈ ᵘⁿᵈᵉʳ ᵗʰᵉ ᵃᵖᵃᶜʰᵉ ˡᶦᶜᵉⁿˢᵉ ᵛᵉʳˢᶦᵒⁿ ᵗʷᵒ ᵈᵒᵗ ᶻᵉʳᵒ */ package io.vavr.collection; import io.vavr.JmhRunner; import org.junit.Test; import org.openjdk.jmh.annotations.*; import java.util.ArrayList; import java.util.Objects; import java.util.stream.Collectors; import static java.util.Arrays.asList; import static io.vavr.JmhRunner.create; import static io.vavr.JmhRunner.getRandomValues; import static io.vavr.collection.Collections.areEqual; import static scala.collection.JavaConverters.asJavaCollection; import static scala.collection.JavaConverters.asScalaBuffer; public class ListBenchmark { static final Array<Class<?>> CLASSES = Array.of( Create.class, Head.class, Tail.class, Get.class, Update.class, Prepend.class, Append.class, GroupBy.class, Iterate.class ); @Test public void testAsserts() { JmhRunner.runDebugWithAsserts(CLASSES); } public static void main(String... args) { JmhRunner.runNormalNoAsserts(CLASSES); } @State(Scope.Benchmark) public static class Base { @Param({ "10", "100", "1000" }) public int CONTAINER_SIZE; int EXPECTED_AGGREGATE; Integer[] ELEMENTS; /* Only use these for non-mutating operations */ java.util.ArrayList<Integer> javaMutable; java.util.LinkedList<Integer> javaMutableLinked; scala.collection.mutable.MutableList<Integer> scalaMutable; fj.data.List<Integer> fjavaPersistent; org.pcollections.PStack<Integer> pcollectionsPersistent; scala.collection.immutable.List<Integer> scalaPersistent; clojure.lang.IPersistentList clojurePersistent; io.vavr.collection.List<Integer> vavrPersistent; @Setup @SuppressWarnings("unchecked") public void setup() { ELEMENTS = getRandomValues(CONTAINER_SIZE, 0); EXPECTED_AGGREGATE = Iterator.of(ELEMENTS).reduce(JmhRunner::aggregate); javaMutable = create(java.util.ArrayList::new, asList(ELEMENTS), v -> areEqual(v, asList(ELEMENTS))); javaMutableLinked = create(java.util.LinkedList::new, asList(ELEMENTS), v -> areEqual(v, asList(ELEMENTS))); scalaMutable = create(v -> (scala.collection.mutable.MutableList<Integer>) scala.collection.mutable.MutableList$.MODULE$.apply(asScalaBuffer(v)), asList(ELEMENTS), v -> areEqual(asJavaCollection(v), javaMutable)); scalaPersistent = create(v -> scala.collection.immutable.List$.MODULE$.apply(asScalaBuffer(v)), javaMutable, v -> areEqual(asJavaCollection(v), javaMutable)); clojurePersistent = create(clojure.lang.PersistentList::create, javaMutable, v -> areEqual((Iterable<?>) v, javaMutable)); fjavaPersistent = create(v -> fj.data.List.fromIterator(v.iterator()), javaMutable, v -> areEqual(v, javaMutable)); pcollectionsPersistent = create(org.pcollections.ConsPStack::from, javaMutable, v -> areEqual(v, javaMutable)); vavrPersistent = create(io.vavr.collection.List::ofAll, javaMutable, v -> areEqual(v, javaMutable)); } } public static class Create extends Base { @Benchmark public Object java_mutable() { final ArrayList<Integer> values = new ArrayList<>(javaMutable); assert areEqual(values, javaMutable); return values; } @Benchmark public Object scala_persistent() { final scala.collection.immutable.List<?> values = scala.collection.immutable.List$.MODULE$.apply(scalaMutable); assert Objects.equals(values, scalaPersistent); return values; } @Benchmark public Object clojure_persistent() { final clojure.lang.IPersistentStack values = clojure.lang.PersistentList.create(javaMutable); assert Objects.equals(values, clojurePersistent); return values; } @Benchmark public Object fjava_persistent() { final fj.data.List<Integer> values = fj.data.List.fromIterator(javaMutable.iterator()); assert areEqual(values, javaMutable); return values; } @Benchmark public Object pcollections_persistent() { final org.pcollections.PStack<Integer> values = org.pcollections.ConsPStack.from(javaMutable); assert areEqual(values, javaMutable); return values; } @Benchmark public Object vavr_persistent() { final io.vavr.collection.List<Integer> values = io.vavr.collection.List.ofAll(javaMutable); assert areEqual(values, javaMutable); return values.head(); } } public static class Head extends Base { @Benchmark public Object java_mutable() { final Object head = javaMutable.get(0); assert Objects.equals(head, ELEMENTS[0]); return head; } @Benchmark public Object scala_persistent() { final Object head = scalaPersistent.head(); assert Objects.equals(head, javaMutable.get(0)); return head; } @Benchmark public Object clojure_persistent() { final Object head = clojurePersistent.peek(); assert Objects.equals(head, javaMutable.get(0)); return head; } @Benchmark public Object fjava_persistent() { final Object head = fjavaPersistent.head(); assert Objects.equals(head, javaMutable.get(0)); return head; } @Benchmark public Object pcollections_persistent() { final Object head = pcollectionsPersistent.get(0); assert Objects.equals(head, javaMutable.get(0)); return head; } @Benchmark public Object vavr_persistent() { final Object head = vavrPersistent.head(); assert Objects.equals(head, javaMutable.get(0)); return head; } } @SuppressWarnings("Convert2MethodRef") public static class Tail extends Base { @State(Scope.Thread) public static class Initialized { final java.util.ArrayList<Integer> javaMutable = new java.util.ArrayList<>(); final java.util.LinkedList<Integer> javaMutableLinked = new java.util.LinkedList<>(); @Setup(Level.Invocation) public void initializeMutable(Base state) { java.util.Collections.addAll(javaMutable, state.ELEMENTS); javaMutableLinked.addAll(javaMutable); assert areEqual(javaMutable, asList(state.ELEMENTS)) && areEqual(javaMutableLinked, javaMutable); } @TearDown(Level.Invocation) public void tearDown() { javaMutable.clear(); javaMutableLinked.clear(); } } @Benchmark public Object java_mutable(Initialized state) { final java.util.ArrayList<Integer> values = state.javaMutable; for (int i = 0; i < CONTAINER_SIZE; i++) { values.remove(0); } assert values.isEmpty(); return values; } @Benchmark public Object java_linked_mutable(Initialized state) { final java.util.LinkedList<Integer> values = state.javaMutableLinked; for (int i = 0; i < CONTAINER_SIZE; i++) { values.remove(0); } assert values.isEmpty(); return values; } @Benchmark @SuppressWarnings({ "unchecked", "RedundantCast" }) public Object scala_persistent() { scala.collection.immutable.List<Integer> values = scalaPersistent; for (int i = 0; i < CONTAINER_SIZE; i++) { values = (scala.collection.immutable.List<Integer>) values.tail(); } assert values.isEmpty(); return values; } @Benchmark public Object clojure_persistent() { clojure.lang.IPersistentStack values = clojurePersistent; for (int i = 0; i < CONTAINER_SIZE; i++) { values = values.pop(); } assert Objects.equals(values, clojure.lang.PersistentList.EMPTY); return values; } @Benchmark public Object fjava_persistent() { fj.data.List<Integer> values = fjavaPersistent; for (int i = 0; i < CONTAINER_SIZE; i++) { values = values.tail(); } assert values.isEmpty(); return values; } @Benchmark public Object pcollections_persistent() { org.pcollections.PStack<Integer> values = pcollectionsPersistent; for (int i = 0; i < CONTAINER_SIZE; i++) { values = values.minus(0); } assert values.isEmpty(); return values; } @Benchmark public Object vavr_persistent() { io.vavr.collection.List<Integer> values = vavrPersistent; for (int i = 0; i < CONTAINER_SIZE; i++) { values = values.tail(); } assert values.isEmpty(); return values; } } public static class Get extends Base { @Benchmark public int java_mutable() { int aggregate = 0; for (int i = 0; i < CONTAINER_SIZE; i++) { aggregate ^= javaMutable.get(i); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int java_linked_mutable() { int aggregate = 0; for (int i = 0; i < CONTAINER_SIZE; i++) { aggregate ^= javaMutableLinked.get(i); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int scala_persistent() { int aggregate = 0; for (int i = 0; i < CONTAINER_SIZE; i++) { aggregate ^= scalaPersistent.apply(i); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int fjava_persistent() { int aggregate = 0; for (int i = 0; i < CONTAINER_SIZE; i++) { aggregate ^= fjavaPersistent.index(i); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int pcollections_persistent() { int aggregate = 0; for (int i = 0; i < CONTAINER_SIZE; i++) { aggregate ^= pcollectionsPersistent.get(i); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int vavr_persistent() { int aggregate = 0; for (int i = 0; i < CONTAINER_SIZE; i++) { aggregate ^= vavrPersistent.get(i); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } } public static class Update extends Base { @State(Scope.Thread) public static class Initialized { final java.util.ArrayList<Integer> javaMutable = new java.util.ArrayList<>(); final java.util.LinkedList<Integer> javaMutableLinked = new java.util.LinkedList<>(); final scala.collection.mutable.MutableList<Integer> scalaMutable = new scala.collection.mutable.MutableList<>(); @Setup(Level.Invocation) public void initializeMutable(Base state) { java.util.Collections.addAll(javaMutable, state.ELEMENTS); java.util.Collections.addAll(javaMutableLinked, state.ELEMENTS); for (int i = state.CONTAINER_SIZE - 1; i >= 0; i--) { scalaMutable.prependElem(state.ELEMENTS[i]); } assert areEqual(javaMutable, asList(state.ELEMENTS)) && areEqual(javaMutableLinked, javaMutable) && areEqual(asJavaCollection(scalaMutable), javaMutable); } @TearDown(Level.Invocation) public void tearDown() { javaMutable.clear(); javaMutableLinked.clear(); scalaMutable.clear(); } } @Benchmark public Object java_mutable(Initialized state) { final java.util.ArrayList<Integer> values = state.javaMutable; for (int i = 0; i < CONTAINER_SIZE; i++) { values.set(i, 0); } assert Array.ofAll(values).forAll(e -> e == 0); return values; } @Benchmark public Object java_linked_mutable(Initialized state) { final java.util.LinkedList<Integer> values = state.javaMutableLinked; for (int i = 0; i < CONTAINER_SIZE; i++) { values.set(i, 0); } assert Array.ofAll(values).forAll(e -> e == 0); return values; } @Benchmark public Object scala_mutable(Initialized state) { final scala.collection.mutable.MutableList<Integer> values = state.scalaMutable; for (int i = 0; i < CONTAINER_SIZE; i++) { values.update(i, 0); } assert Array.ofAll(asJavaCollection(values)).forAll(e -> e == 0); return values; } @Benchmark public Object pcollections_persistent() { org.pcollections.PStack<Integer> values = pcollectionsPersistent; for (int i = 0; i < CONTAINER_SIZE; i++) { values = values.with(i, 0); } assert Array.ofAll(values).forAll(e -> e == 0); return values; } @Benchmark public Object vavr_persistent() { io.vavr.collection.List<Integer> values = vavrPersistent; for (int i = 0; i < CONTAINER_SIZE; i++) { values = values.update(i, 0); } assert values.forAll(e -> e == 0); return values; } } @SuppressWarnings("ManualArrayToCollectionCopy") public static class Prepend extends Base { @Benchmark public Object java_mutable() { final java.util.ArrayList<Integer> values = new java.util.ArrayList<>(CONTAINER_SIZE); for (Integer element : ELEMENTS) { values.add(0, element); } assert areEqual(Array.ofAll(values).reverse(), javaMutable); return values; } @Benchmark public Object java_linked_mutable() { final java.util.LinkedList<Integer> values = new java.util.LinkedList<>(); for (Integer element : ELEMENTS) { values.addFirst(element); } assert areEqual(Array.ofAll(values).reverse(), javaMutable); return values; } @Benchmark public Object scala_mutable() { final scala.collection.mutable.MutableList<Integer> values = new scala.collection.mutable.MutableList<>(); for (Integer element : ELEMENTS) { values.prependElem(element); } assert areEqual(Array.ofAll(asJavaCollection(values)).reverse(), javaMutable); return values; } @Benchmark public Object scala_persistent() { scala.collection.immutable.List<Integer> values = scala.collection.immutable.List$.MODULE$.empty(); for (Integer element : ELEMENTS) { values = values.$colon$colon(element); } assert areEqual(Array.ofAll(asJavaCollection(values)).reverse(), javaMutable); return values; } @Benchmark public Object fjava_persistent() { fj.data.List<Integer> values = fj.data.List.list(); for (Integer element : ELEMENTS) { values = values.cons(element); } assert areEqual(Array.ofAll(values).reverse(), javaMutable); return values; } @Benchmark public Object pcollections_persistent() { org.pcollections.PStack<Integer> values = org.pcollections.ConsPStack.empty(); for (Integer element : ELEMENTS) { values = values.plus(element); } assert areEqual(Array.ofAll(values).reverse(), javaMutable); return values; } @Benchmark public Object vavr_persistent() { io.vavr.collection.List<Integer> values = io.vavr.collection.List.empty(); for (Integer element : ELEMENTS) { values = values.prepend(element); } assert areEqual(values.reverse(), javaMutable); return values; } } @SuppressWarnings("ManualArrayToCollectionCopy") public static class Append extends Base { @Benchmark public Object java_mutable() { final java.util.ArrayList<Integer> values = new java.util.ArrayList<>(CONTAINER_SIZE); for (Integer element : ELEMENTS) { values.add(element); } assert areEqual(values, javaMutable); return values; } @Benchmark public Object java_linked_mutable() { final java.util.LinkedList<Integer> values = new java.util.LinkedList<>(); for (Integer element : ELEMENTS) { values.addLast(element); } assert values.size() == CONTAINER_SIZE; return values; } @Benchmark public Object scala_mutable() { final scala.collection.mutable.MutableList<Integer> values = new scala.collection.mutable.MutableList<>(); for (Integer element : ELEMENTS) { values.appendElem(element); } assert areEqual(asJavaCollection(values), javaMutable); return values; } @Benchmark public Object fjava_persistent() { fj.data.List<Integer> values = fj.data.List.list(); for (Integer element : ELEMENTS) { values = values.snoc(element); } assert areEqual(values, javaMutable); return values; } @Benchmark public Object pcollections_persistent() { org.pcollections.PStack<Integer> values = org.pcollections.ConsPStack.empty(); for (Integer element : ELEMENTS) { values = values.plus(values.size(), element); } assert areEqual(values, javaMutable); return values; } @Benchmark public Object vavr_persistent() { io.vavr.collection.List<Integer> values = io.vavr.collection.List.empty(); for (Integer element : ELEMENTS) { values = values.append(element); } assert areEqual(values, javaMutable); return values; } } public static class GroupBy extends Base { @Benchmark public Object java_mutable() { return javaMutable.stream().collect(Collectors.groupingBy(Integer::bitCount)); } @Benchmark public Object scala_persistent() { return scalaPersistent.groupBy(Integer::bitCount); } @Benchmark public Object fjava_persistent() { return fjavaPersistent.groupBy(Integer::bitCount); } @Benchmark public Object vavr_persistent() { return vavrPersistent.groupBy(Integer::bitCount); } } @SuppressWarnings("ForLoopReplaceableByForEach") public static class Iterate extends Base { @Benchmark public int java_mutable() { int aggregate = 0; for (final java.util.Iterator<Integer> iterator = javaMutable.iterator(); iterator.hasNext(); ) { aggregate ^= iterator.next(); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int java_linked_mutable() { int aggregate = 0; for (final java.util.Iterator<Integer> iterator = javaMutableLinked.iterator(); iterator.hasNext(); ) { aggregate ^= iterator.next(); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int scala_mutable() { int aggregate = 0; for (final scala.collection.Iterator<Integer> iterator = scalaMutable.iterator(); iterator.hasNext(); ) { aggregate ^= iterator.next(); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int scala_persistent() { int aggregate = 0; for (final scala.collection.Iterator<Integer> iterator = scalaPersistent.iterator(); iterator.hasNext(); ) { aggregate ^= iterator.next(); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int fjava_persistent() { int aggregate = 0; for (final java.util.Iterator<Integer> iterator = fjavaPersistent.iterator(); iterator.hasNext(); ) { aggregate ^= iterator.next(); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int pcollections_persistent() { int aggregate = 0; for (final java.util.Iterator<Integer> iterator = pcollectionsPersistent.iterator(); iterator.hasNext(); ) { aggregate ^= iterator.next(); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } @Benchmark public int vavr_persistent() { int aggregate = 0; for (final java.util.Iterator<Integer> iterator = vavrPersistent.iterator(); iterator.hasNext(); ) { aggregate ^= iterator.next(); } assert aggregate == EXPECTED_AGGREGATE; return aggregate; } } }