/* * Copyright 2015 Goldman Sachs. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.gs.collections.impl.parallel; import java.text.NumberFormat; import java.util.Arrays; import java.util.concurrent.atomic.AtomicInteger; import com.gs.collections.api.block.function.Function; import com.gs.collections.api.block.function.Function0; import com.gs.collections.api.block.function.Function2; import com.gs.collections.api.block.predicate.Predicate; import com.gs.collections.api.block.procedure.Procedure; import com.gs.collections.api.block.procedure.Procedure2; import com.gs.collections.api.list.MutableList; import com.gs.collections.api.set.MutableSet; import com.gs.collections.api.tuple.Pair; import com.gs.collections.impl.ParallelTests; import com.gs.collections.impl.block.factory.IntegerPredicates; import com.gs.collections.impl.block.factory.Predicates; import com.gs.collections.impl.block.factory.Procedures; import com.gs.collections.impl.forkjoin.FJIterate; import com.gs.collections.impl.list.Interval; import com.gs.collections.impl.list.mutable.CompositeFastList; import com.gs.collections.impl.list.mutable.FastList; import com.gs.collections.impl.list.primitive.IntInterval; import com.gs.collections.impl.multimap.bag.HashBagMultimap; import com.gs.collections.impl.set.mutable.UnifiedSet; import com.gs.collections.impl.test.Verify; import com.gs.collections.impl.tuple.Tuples; import com.gs.collections.impl.utility.Iterate; import org.apache.commons.lang.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SerialParallelPerformanceTest { public static final Predicate<Integer> PREDICATE_1 = Predicates.greaterThan(0).and(IntegerPredicates.isOdd()); public static final Predicate<Integer> PREDICATE_2 = IntegerPredicates.isPositive().and(IntegerPredicates.isEven()); public static final Predicate<Integer> PREDICATE_3 = IntegerPredicates.isOdd().and(IntegerPredicates.isNegative()); public static final MutableList<Predicate<Integer>> PREDICATES = FastList.newListWith(PREDICATE_1, PREDICATE_2, PREDICATE_3); private static final Logger LOGGER = LoggerFactory.getLogger(SerialParallelPerformanceTest.class); private static final int SCALE_FACTOR = Integer.parseInt(System.getProperty("scaleFactor", "100")); private static final int WARM_UP_COUNT = Integer.parseInt(System.getProperty("WarmupCount", "100")); private static final int PARALLEL_RUN_COUNT = Integer.parseInt(System.getProperty("ParallelRunCount", "200")); private static final int SERIAL_RUN_COUNT = Integer.parseInt(System.getProperty("SerialRunCount", "200")); private static final int SMALL_COUNT = 100 * SCALE_FACTOR; private static final int MEDIUM_COUNT = 1000 * SCALE_FACTOR; private static final int LARGE_COUNT = 10000 * SCALE_FACTOR; private static final Function<Integer, Pair<Integer, Integer>> PAIR_FUNCTION = value -> Tuples.pair(value, value); private static final Function2<Integer, String, Integer> COUNT_AGGREGATOR = (aggregate, word) -> aggregate + 1; @Test @Category(ParallelTests.class) public void select() { this.measureAlgorithmForIntegerIterable("Select", each -> this.select(each.value())); } @Test @Category(ParallelTests.class) public void reject() { this.measureAlgorithmForIntegerIterable("Reject", each -> this.reject(each.value())); } @Test @Category(ParallelTests.class) public void count() { this.measureAlgorithmForIntegerIterable("Count", each -> this.count(each.value())); } @Test @Category(ParallelTests.class) public void collectIf() { this.measureAlgorithmForIntegerIterable("CollectIf", each -> this.collectIf(each.value())); } @Test @Category(ParallelTests.class) public void collect() { this.measureAlgorithmForIntegerIterable("Collect", each -> this.collect(each.value())); } @Test @Category(ParallelTests.class) public void groupBy() { this.measureAlgorithmForRandomStringIterable("GroupBy", each -> this.groupBy(each.value())); } @Test @Category(ParallelTests.class) public void aggregateBy() { this.measureAlgorithmForRandomStringIterable("AggregateBy", each -> this.aggregateBy(each.value())); } @Test @Category(ParallelTests.class) public void aggregateInPlaceBy() { this.measureAlgorithmForRandomStringIterable("AggregateInPlaceBy", each -> this.aggregateInPlaceBy(each.value())); } public MutableList<String> generateWordsList(int count) { FastList<String> words = FastList.newList(); Interval.oneTo(count).forEach((int each) -> words.add(RandomStringUtils.randomAlphabetic(2))); return words; } public MutableSet<String> generateWordsSet(int count) { UnifiedSet<String> words = UnifiedSet.newSet(); while (words.size() < count) { words.add(RandomStringUtils.randomAlphabetic(5)); } return words; } @After public void tearDown() { SerialParallelPerformanceTest.forceGC(); } private static void forceGC() { IntInterval.oneTo(20).forEach(each -> { System.gc(); try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } }); } public void printMachineAndTestConfiguration(String serialParallelAlgorithm) { LOGGER.info("*** Algorithm: {}", serialParallelAlgorithm); LOGGER.info("Available Processors: {}", Runtime.getRuntime().availableProcessors()); LOGGER.info("Default Thread Pool Size: {}", ParallelIterate.getDefaultMaxThreadPoolSize()); LOGGER.info("Default Task Count: {}", ParallelIterate.getDefaultTaskCount()); LOGGER.info("Scale Factor: {}", SCALE_FACTOR); LOGGER.info("Warm up count: {}", WARM_UP_COUNT); LOGGER.info("Parallel Run Count: {}", PARALLEL_RUN_COUNT); LOGGER.info("Serial** Run Count: {}", SERIAL_RUN_COUNT); } private MutableList<Integer> getSizes() { return FastList.newListWith(LARGE_COUNT, MEDIUM_COUNT, SMALL_COUNT).shuffleThis(); } private MutableList<Function0<Iterable<Integer>>> getIntegerListGenerators(int count) { Interval interval = Interval.fromTo(-(count / 2), count / 2 - 1); MutableList<Function0<Iterable<Integer>>> generators = FastList.newList(); generators.add(() -> { return interval.toList().shuffleThis(); }); generators.add(() -> { MutableList<Integer> integers = interval.toList().shuffleThis(); return integers.toImmutable(); }); generators.add(interval::toSet); return generators.shuffleThis(); } private MutableList<Function0<Iterable<String>>> getRandomWordsGenerators(int count) { MutableList<Function0<Iterable<String>>> generators = FastList.newList(); generators.add(() -> this.generateWordsList(count)); generators.add(() -> this.generateWordsList(count).toImmutable()); generators.add(() -> this.generateWordsSet(count)); return generators.shuffleThis(); } private void measureAlgorithmForIntegerIterable(String algorithmName, Procedure<Function0<Iterable<Integer>>> algorithm) { this.printMachineAndTestConfiguration(algorithmName); for (int i = 0; i < 4; i++) { this.getSizes().forEach(Procedures.cast(count -> this.getIntegerListGenerators(count).forEach(algorithm))); } } private void measureAlgorithmForRandomStringIterable(String algorithmName, Procedure<Function0<Iterable<String>>> algorithm) { this.printMachineAndTestConfiguration(algorithmName); for (int i = 0; i < 4; i++) { this.getSizes().forEach(Procedures.cast(count -> this.getRandomWordsGenerators(count).forEach(algorithm))); } } private void aggregateBy(Iterable<String> words) { MutableList<Runnable> runnables = FastList.newList(); runnables.add(() -> this.basicSerialAggregateByPerformance(words, SERIAL_RUN_COUNT)); runnables.add(() -> this.basicParallelAggregateByPerformance(words, PARALLEL_RUN_COUNT)); runnables.add(() -> this.basicForkJoinAggregateByPerformance(words, PARALLEL_RUN_COUNT)); this.shuffleAndRun(runnables); } private void aggregateInPlaceBy(Iterable<String> words) { MutableList<Runnable> runnables = FastList.newList(); runnables.add(() -> this.basicSerialAggregateInPlaceByPerformance(words, SERIAL_RUN_COUNT)); runnables.add(() -> this.basicParallelAggregateInPlaceByPerformance(words, PARALLEL_RUN_COUNT)); runnables.add(() -> this.basicForkJoinAggregateInPlaceByPerformance(words, PARALLEL_RUN_COUNT)); this.shuffleAndRun(runnables); } private void groupBy(Iterable<String> words) { MutableList<Runnable> runnables = FastList.newList(); runnables.add(() -> this.basicSerialGroupByPerformance(words, SERIAL_RUN_COUNT)); runnables.add(() -> this.basicParallelGroupByPerformance(words, PARALLEL_RUN_COUNT)); runnables.add(() -> this.basicForkJoinGroupByPerformance(words, PARALLEL_RUN_COUNT)); this.shuffleAndRun(runnables); } private void collect(Iterable<Integer> collection) { MutableList<Runnable> runnables = FastList.newList(); runnables.add(() -> this.basicSerialCollectPerformance(collection, SERIAL_RUN_COUNT)); runnables.add(() -> this.basicParallelCollectPerformance(collection, PARALLEL_RUN_COUNT)); runnables.add(() -> this.basicForkJoinCollectPerformance(collection, PARALLEL_RUN_COUNT)); this.shuffleAndRun(runnables); } private void collectIf(Iterable<Integer> collection) { MutableList<Runnable> runnables = FastList.newList(); runnables.add(() -> this.basicSerialCollectIfPerformance(collection, PREDICATES, SERIAL_RUN_COUNT)); runnables.add(() -> this.basicParallelCollectIfPerformance(collection, PREDICATES, PARALLEL_RUN_COUNT)); runnables.add(() -> this.basicForkJoinCollectIfPerformance(collection, PREDICATES, PARALLEL_RUN_COUNT)); this.shuffleAndRun(runnables); } private void count(Iterable<Integer> collection) { MutableList<Runnable> runnables = FastList.newList(); runnables.add(() -> this.basicSerialCountPerformance(collection, PREDICATES, SERIAL_RUN_COUNT)); runnables.add(() -> this.basicParallelCountPerformance(collection, PREDICATES, PARALLEL_RUN_COUNT)); runnables.add(() -> this.basicForkJoinCountPerformance(collection, PREDICATES, PARALLEL_RUN_COUNT)); this.shuffleAndRun(runnables); } private void shuffleAndRun(MutableList<Runnable> runnables) { runnables.shuffleThis().forEach(Procedures.cast(Runnable::run)); } private void reject(Iterable<Integer> collection) { MutableList<Runnable> runnables = FastList.newList(); runnables.add(() -> this.basicSerialRejectPerformance(collection, PREDICATES, SERIAL_RUN_COUNT)); runnables.add(() -> this.basicParallelRejectPerformance(collection, PREDICATES, PARALLEL_RUN_COUNT)); runnables.add(() -> this.basicForkJoinRejectPerformance(collection, PREDICATES, PARALLEL_RUN_COUNT)); this.shuffleAndRun(runnables); } private void select(Iterable<Integer> collection) { MutableList<Runnable> runnables = FastList.newList(); runnables.add(() -> this.basicSerialSelectPerformance(collection, PREDICATES, SERIAL_RUN_COUNT)); runnables.add(() -> this.basicParallelSelectPerformance(collection, PREDICATES, PARALLEL_RUN_COUNT)); runnables.add(() -> this.basicForkJoinSelectPerformance(collection, PREDICATES, PARALLEL_RUN_COUNT)); this.shuffleAndRun(runnables); } private double basicSerialSelectPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicateList, int count) { return TimeKeeper.logAverageMillisecondsToRun("Serial** Select: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(Iterate.select(iterable, predicateList.get(0), FastList.<Integer>newList())); Verify.assertNotEmpty(Iterate.select(iterable, predicateList.get(1), FastList.<Integer>newList())); Verify.assertNotEmpty(Iterate.select(iterable, predicateList.get(2), FastList.<Integer>newList())); }, count, WARM_UP_COUNT); } private String formatSizeOf(Iterable<?> iterable) { return NumberFormat.getInstance().format(Iterate.sizeOf(iterable)); } private double basicParallelSelectPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicateList, int count) { return TimeKeeper.logAverageMillisecondsToRun("Parallel Select: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(ParallelIterate.select( iterable, predicateList.get(0), new CompositeFastList<>(), true)); Verify.assertNotEmpty(ParallelIterate.select( iterable, predicateList.get(1), new CompositeFastList<>(), true)); Verify.assertNotEmpty(ParallelIterate.select( iterable, predicateList.get(2), new CompositeFastList<>(), true)); }, count, WARM_UP_COUNT); } private double basicForkJoinSelectPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicateList, int count) { return TimeKeeper.logAverageMillisecondsToRun("ForkJoin Select: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(FJIterate.select( iterable, predicateList.get(0), new CompositeFastList<>(), true)); Verify.assertNotEmpty(FJIterate.select( iterable, predicateList.get(1), new CompositeFastList<>(), true)); Verify.assertNotEmpty(FJIterate.select( iterable, predicateList.get(2), new CompositeFastList<>(), true)); }, count, WARM_UP_COUNT); } private String getSimpleName(Object collection) { return collection.getClass().getSimpleName(); } private double basicSerialCountPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicateList, int count) { return TimeKeeper.logAverageMillisecondsToRun("Serial** Count: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Assert.assertTrue(Iterate.count(iterable, predicateList.get(0)) > 0); Assert.assertTrue(Iterate.count(iterable, predicateList.get(1)) > 0); Assert.assertTrue(Iterate.count(iterable, predicateList.get(2)) > 0); }, count, WARM_UP_COUNT); } private double basicParallelCountPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicateList, int count) { return TimeKeeper.logAverageMillisecondsToRun("Parallel Count: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Assert.assertTrue(ParallelIterate.count(iterable, predicateList.get(0)) > 0); Assert.assertTrue(ParallelIterate.count(iterable, predicateList.get(1)) > 0); Assert.assertTrue(ParallelIterate.count(iterable, predicateList.get(2)) > 0); }, count, WARM_UP_COUNT); } private double basicForkJoinCountPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicateList, int count) { return TimeKeeper.logAverageMillisecondsToRun("ForkJoin Count: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Assert.assertTrue(FJIterate.count(iterable, predicateList.get(0)) > 0); Assert.assertTrue(FJIterate.count(iterable, predicateList.get(1)) > 0); Assert.assertTrue(FJIterate.count(iterable, predicateList.get(2)) > 0); }, count, WARM_UP_COUNT); } private double basicSerialRejectPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicateList, int count) { return TimeKeeper.logAverageMillisecondsToRun("Serial** Reject: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(Iterate.reject(iterable, predicateList.get(0), FastList.<Integer>newList())); Verify.assertNotEmpty(Iterate.reject(iterable, predicateList.get(1), FastList.<Integer>newList())); Verify.assertNotEmpty(Iterate.reject(iterable, predicateList.get(2), FastList.<Integer>newList())); }, count, WARM_UP_COUNT); } private double basicParallelRejectPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicateList, int count) { return TimeKeeper.logAverageMillisecondsToRun("Parallel Reject: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(ParallelIterate.reject( iterable, predicateList.get(0), new CompositeFastList<>(), true)); Verify.assertNotEmpty(ParallelIterate.reject( iterable, predicateList.get(1), new CompositeFastList<>(), true)); Verify.assertNotEmpty(ParallelIterate.reject( iterable, predicateList.get(2), new CompositeFastList<>(), true)); }, count, WARM_UP_COUNT); } private double basicForkJoinRejectPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicateList, int count) { return TimeKeeper.logAverageMillisecondsToRun("ForkJoin Reject: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(FJIterate.reject( iterable, predicateList.get(0), new CompositeFastList<>(), true)); Verify.assertNotEmpty(FJIterate.reject( iterable, predicateList.get(1), new CompositeFastList<>(), true)); Verify.assertNotEmpty(FJIterate.reject( iterable, predicateList.get(2), new CompositeFastList<>(), true)); }, count, WARM_UP_COUNT); } private double basicParallelCollectIfPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicates, int count) { return TimeKeeper.logAverageMillisecondsToRun("Parallel CollectIf: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(ParallelIterate.collectIf( iterable, predicates.get(0), PAIR_FUNCTION, new CompositeFastList<>(), true)); Verify.assertNotEmpty(ParallelIterate.collectIf( iterable, predicates.get(1), Integer::longValue, new CompositeFastList<>(), true)); Verify.assertNotEmpty(ParallelIterate.collectIf( iterable, predicates.get(0), Integer::shortValue, new CompositeFastList<>(), true)); }, count, WARM_UP_COUNT); } private double basicForkJoinCollectIfPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicates, int count) { return TimeKeeper.logAverageMillisecondsToRun("ForkJoin CollectIf: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(FJIterate.collectIf( iterable, predicates.get(0), PAIR_FUNCTION, new CompositeFastList<>(), true)); Verify.assertNotEmpty(FJIterate.collectIf( iterable, predicates.get(1), Integer::longValue, new CompositeFastList<>(), true)); Verify.assertNotEmpty(FJIterate.collectIf( iterable, predicates.get(0), Integer::shortValue, new CompositeFastList<>(), true)); }, count, WARM_UP_COUNT); } private double basicSerialCollectIfPerformance( Iterable<Integer> iterable, MutableList<Predicate<Integer>> predicates, int count) { return TimeKeeper.logAverageMillisecondsToRun("Serial** CollectIf: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(Iterate.collectIf( iterable, predicates.get(0), PAIR_FUNCTION, FastList.<Pair<Integer, Integer>>newList())); Verify.assertNotEmpty(Iterate.collectIf( iterable, predicates.get(1), Integer::longValue, FastList.<Long>newList())); Verify.assertNotEmpty(Iterate.collectIf( iterable, predicates.get(2), Integer::shortValue, FastList.<Short>newList())); }, count, WARM_UP_COUNT); } private double basicSerialCollectPerformance( Iterable<Integer> iterable, int count) { return TimeKeeper.logAverageMillisecondsToRun("Serial** Collect: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { int initialCapacity = Iterate.sizeOf(iterable); Verify.assertNotEmpty(Iterate.collect( iterable, PAIR_FUNCTION, FastList.<Pair<Integer, Integer>>newList(initialCapacity))); Verify.assertNotEmpty(Iterate.collect( iterable, Integer::longValue, FastList.<Long>newList(initialCapacity))); Verify.assertNotEmpty(Iterate.collect( iterable, Integer::shortValue, FastList.<Short>newList(initialCapacity))); }, count, 10); } private double basicSerialGroupByPerformance( Iterable<String> iterable, int count) { Assert.assertEquals(HashBagMultimap.newMultimap(ParallelIterate.groupBy(iterable, Alphagram::new)), HashBagMultimap.newMultimap(Iterate.groupBy(iterable, Alphagram::new))); return TimeKeeper.logAverageMillisecondsToRun("Serial** GroupBy: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> Verify.assertNotEmpty(Iterate.groupBy( iterable, Alphagram::new)), count, 10); } private double basicSerialAggregateInPlaceByPerformance( Iterable<String> iterable, int count) { Assert.assertEquals( ParallelIterate.aggregateInPlaceBy(iterable, Alphagram::new, (Function0<AtomicIntegerWithEquals>) () -> new AtomicIntegerWithEquals(0), (Procedure2<AtomicIntegerWithEquals, String>) (value, each) -> value.incrementAndGet()), Iterate.aggregateInPlaceBy(iterable, Alphagram::new, (Function0<AtomicIntegerWithEquals>) () -> new AtomicIntegerWithEquals(0), (Procedure2<AtomicIntegerWithEquals, String>) (value, each) -> value.incrementAndGet())); return TimeKeeper.logAverageMillisecondsToRun("Serial** AggregateInPlaceBy: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> Verify.assertNotEmpty( Iterate.aggregateInPlaceBy( iterable, Alphagram::new, (Function0<AtomicIntegerWithEquals>) () -> new AtomicIntegerWithEquals(0), (Procedure2<AtomicIntegerWithEquals, String>) (value, each) -> value.incrementAndGet())), count, 10); } private double basicSerialAggregateByPerformance( Iterable<String> iterable, int count) { Assert.assertEquals( ParallelIterate.aggregateBy(iterable, Alphagram::new, () -> 0, COUNT_AGGREGATOR), Iterate.aggregateBy(iterable, Alphagram::new, () -> 0, COUNT_AGGREGATOR)); return TimeKeeper.logAverageMillisecondsToRun("Serial** AggregateBy: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> Verify.assertNotEmpty( Iterate.aggregateBy( iterable, Alphagram::new, () -> 0, COUNT_AGGREGATOR)), count, 10); } private double basicParallelCollectPerformance(Iterable<Integer> iterable, int count) { return TimeKeeper.logAverageMillisecondsToRun("Parallel Collect: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(ParallelIterate.collect( iterable, PAIR_FUNCTION, new CompositeFastList<>(), true)); Verify.assertNotEmpty(ParallelIterate.collect( iterable, Integer::longValue, new CompositeFastList<>(), true)); Verify.assertNotEmpty(ParallelIterate.collect( iterable, Integer::shortValue, new CompositeFastList<>(), true)); }, count, WARM_UP_COUNT); } private double basicForkJoinCollectPerformance(Iterable<Integer> iterable, int count) { return TimeKeeper.logAverageMillisecondsToRun("ForkJoin Collect: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> { Verify.assertNotEmpty(FJIterate.collect( iterable, PAIR_FUNCTION, new CompositeFastList<>(), true)); Verify.assertNotEmpty(FJIterate.collect( iterable, Integer::longValue, new CompositeFastList<>(), true)); Verify.assertNotEmpty(FJIterate.collect( iterable, Integer::shortValue, new CompositeFastList<>(), true)); }, count, WARM_UP_COUNT); } private double basicParallelGroupByPerformance(Iterable<String> iterable, int count) { Assert.assertEquals(HashBagMultimap.newMultimap(ParallelIterate.groupBy(iterable, Alphagram::new)), HashBagMultimap.newMultimap(Iterate.groupBy(iterable, Alphagram::new))); return TimeKeeper.logAverageMillisecondsToRun("Parallel GroupBy: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> Verify.assertNotEmpty(ParallelIterate.groupBy( iterable, Alphagram::new)), count, WARM_UP_COUNT); } private double basicForkJoinGroupByPerformance(Iterable<String> iterable, int count) { Assert.assertEquals(HashBagMultimap.newMultimap(FJIterate.groupBy(iterable, Alphagram::new)), HashBagMultimap.newMultimap(Iterate.groupBy(iterable, Alphagram::new))); return TimeKeeper.logAverageMillisecondsToRun("ForkJoin GroupBy: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> Verify.assertNotEmpty(FJIterate.groupBy( iterable, Alphagram::new)), count, WARM_UP_COUNT); } private double basicParallelAggregateInPlaceByPerformance(Iterable<String> iterable, int count) { Assert.assertEquals( ParallelIterate.aggregateInPlaceBy(iterable, Alphagram::new, (Function0<AtomicIntegerWithEquals>) () -> new AtomicIntegerWithEquals(0), (Procedure2<AtomicIntegerWithEquals, String>) (value, each) -> value.incrementAndGet()), Iterate.aggregateInPlaceBy(iterable, Alphagram::new, (Function0<AtomicIntegerWithEquals>) () -> new AtomicIntegerWithEquals(0), (Procedure2<AtomicIntegerWithEquals, String>) (value, each) -> value.incrementAndGet())); return TimeKeeper.logAverageMillisecondsToRun("Parallel AggregateInPlaceBy: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> Verify.assertNotEmpty( ParallelIterate.aggregateInPlaceBy( iterable, Alphagram::new, (Function0<AtomicIntegerWithEquals>) () -> new AtomicIntegerWithEquals(0), (Procedure2<AtomicIntegerWithEquals, String>) (value, each) -> value.incrementAndGet())), count, WARM_UP_COUNT); } private double basicForkJoinAggregateInPlaceByPerformance(Iterable<String> iterable, int count) { Assert.assertEquals( FJIterate.aggregateInPlaceBy(iterable, Alphagram::new, (Function0<AtomicIntegerWithEquals>) () -> new AtomicIntegerWithEquals(0), (Procedure2<AtomicIntegerWithEquals, String>) (value, each) -> value.incrementAndGet()), Iterate.aggregateInPlaceBy(iterable, Alphagram::new, (Function0<AtomicIntegerWithEquals>) () -> new AtomicIntegerWithEquals(0), (Procedure2<AtomicIntegerWithEquals, String>) (value, each) -> value.incrementAndGet())); return TimeKeeper.logAverageMillisecondsToRun("ForkJoin AggregateInPlaceBy: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> Verify.assertNotEmpty( FJIterate.aggregateInPlaceBy( iterable, Alphagram::new, (Function0<AtomicIntegerWithEquals>) () -> new AtomicIntegerWithEquals(0), (Procedure2<AtomicIntegerWithEquals, String>) (value, each) -> value.incrementAndGet())), count, WARM_UP_COUNT); } private double basicParallelAggregateByPerformance(Iterable<String> iterable, int count) { Assert.assertEquals( ParallelIterate.aggregateBy(iterable, Alphagram::new, () -> 0, COUNT_AGGREGATOR), Iterate.aggregateBy(iterable, Alphagram::new, () -> 0, COUNT_AGGREGATOR)); return TimeKeeper.logAverageMillisecondsToRun("Parallel AggregateBy: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> Verify.assertNotEmpty( ParallelIterate.aggregateBy(iterable, Alphagram::new, () -> 0, COUNT_AGGREGATOR)), count, WARM_UP_COUNT); } private double basicForkJoinAggregateByPerformance(Iterable<String> iterable, int count) { Assert.assertEquals( FJIterate.aggregateBy(iterable, Alphagram::new, () -> 0, COUNT_AGGREGATOR), Iterate.aggregateBy(iterable, Alphagram::new, () -> 0, COUNT_AGGREGATOR)); return TimeKeeper.logAverageMillisecondsToRun("ForkJoin AggregateBy: " + this.getSimpleName(iterable) + " size: " + this.formatSizeOf(iterable), () -> Verify.assertNotEmpty( FJIterate.aggregateBy(iterable, Alphagram::new, () -> 0, COUNT_AGGREGATOR)), count, WARM_UP_COUNT); } static final class TimeKeeper { private static final SystemTimeProvider PROVIDER = new SystemTimeProvider(); private static final long PAIN_THRESHOLD = 10000L; private static final int MILLIS_TO_NANOS = 1000000; private TimeKeeper() { throw new AssertionError("Suppress default constructor for noninstantiability"); } /** * This method can take either a Runnable or a RunnableWithSetup. In the case of RunnableWithSetup, the setup * method will be called first, without impacting the timing. */ public static long millisecondsToRun(Runnable runnable) { return TimeKeeper.nanosecondsToRun(runnable) / (long) MILLIS_TO_NANOS; } public static long currentTimeNanoseconds() { return PROVIDER.currentTimeNanoseconds(); } public static long currentTimeMilliseconds() { return PROVIDER.currentTimeMilliseconds(); } /** * This method can take either a Runnable or a RunnableWithSetup. In the case of RunnableWithSetup, the setup * method will be called first, without impacting the timing. */ public static long nanosecondsToRun(Runnable runnable) { long start = TimeKeeper.getCurrentTimeAsNanos(); runnable.run(); long end = TimeKeeper.getCurrentTimeAsNanos(); return end - start; } private static long getCurrentTimeAsNanos() { return TimeKeeper.currentTimeNanoseconds(); } private static void doLog(String message, int count, long total, double average) { LOGGER.info("{} Count: {} Total(ms): {} Avg(ms): {}", message, count, TimeKeeper.longNanosToMillisString(total), TimeKeeper.doubleNanosToMillisString(average)); } public static double logAverageMillisecondsToRun( String message, Runnable runnable, int count) { long start = TimeKeeper.getCurrentTimeAsNanos(); for (int i = 0; i < count; i++) { runnable.run(); } long totalNanos = TimeKeeper.getCurrentTimeAsNanos() - start; double averageTime = (double) totalNanos / (double) count; TimeKeeper.doLog(message, count, totalNanos, averageTime); return averageTime / (double) TimeKeeper.MILLIS_TO_NANOS; } private static String doubleNanosToMillisString(double nanos) { return NumberFormat.getInstance().format(nanos / MILLIS_TO_NANOS); } private static String longNanosToMillisString(long nanos) { return NumberFormat.getInstance().format(nanos / MILLIS_TO_NANOS); } public static double logAverageMillisecondsToRun( String message, Runnable runnable, int count, int warmUpCount) { TimeKeeper.warmUp(warmUpCount, runnable); TimeKeeper.gcAndYield(); return TimeKeeper.logAverageMillisecondsToRun(message, runnable, count); } private static void gcAndYield() { SerialParallelPerformanceTest.forceGC(); } private static void warmUp(int warmUpCount, Runnable runnable) { long start = TimeKeeper.currentTimeMilliseconds(); for (int i = 0; i < warmUpCount; i++) { TimeKeeper.millisecondsToRun(runnable); if (TimeKeeper.currentTimeMilliseconds() - start > PAIN_THRESHOLD) { break; } } } private static class SystemTimeProvider { public long currentTimeMilliseconds() { return System.currentTimeMillis(); } public long currentTimeNanoseconds() { return System.nanoTime(); } } } private static final class Alphagram { private final char[] key; private final int hashCode; private Alphagram(String string) { this.key = string.toLowerCase().toCharArray(); Arrays.sort(this.key); this.hashCode = Arrays.hashCode(this.key); } @Override public boolean equals(Object o) { return this == o || Arrays.equals(this.key, ((Alphagram) o).key); } @Override public int hashCode() { return this.hashCode; } @Override public String toString() { return new String(this.key); } } public static final class AtomicIntegerWithEquals extends AtomicInteger { private AtomicIntegerWithEquals(int initialValue) { super(initialValue); } @Override public int hashCode() { return this.get(); } @Override public boolean equals(Object obj) { return (obj instanceof AtomicIntegerWithEquals) && ((AtomicIntegerWithEquals) obj).get() == this.get(); } } }