From a17f939fd9f9e31b1ca688f61543931cb0d3a8a5 Mon Sep 17 00:00:00 2001 From: Bruce Eckel Date: Tue, 9 Aug 2016 10:21:47 -0600 Subject: [PATCH] Added the rest of the performance tests Will probably need tweaking. --- .../RandomBounds.java | 16 +- .../{Lists.java => ListOps.java} | 4 +- understandingcollections/ListPerformance.java | 211 ------------------ .../{Maps.java => MapOps.java} | 4 +- understandingcollections/SetPerformance.java | 70 ------ understandingcollections/Test.java | 13 -- understandingcollections/TestParam.java | 30 --- understandingcollections/Tester.java | 84 ------- understandingcollections/jmhtests/Lists.java | 81 +++++++ understandingcollections/jmhtests/Maps.java | 7 +- understandingcollections/jmhtests/Queues.java | 62 +++++ understandingcollections/jmhtests/Sets.java | 63 ++++++ 12 files changed, 217 insertions(+), 428 deletions(-) rename {understandingcollections => control}/RandomBounds.java (69%) rename understandingcollections/{Lists.java => ListOps.java} (98%) delete mode 100644 understandingcollections/ListPerformance.java rename understandingcollections/{Maps.java => MapOps.java} (97%) delete mode 100644 understandingcollections/SetPerformance.java delete mode 100644 understandingcollections/Test.java delete mode 100644 understandingcollections/TestParam.java delete mode 100644 understandingcollections/Tester.java create mode 100644 understandingcollections/jmhtests/Lists.java create mode 100644 understandingcollections/jmhtests/Queues.java create mode 100644 understandingcollections/jmhtests/Sets.java diff --git a/understandingcollections/RandomBounds.java b/control/RandomBounds.java similarity index 69% rename from understandingcollections/RandomBounds.java rename to control/RandomBounds.java index b464854d..3ea7e966 100644 --- a/understandingcollections/RandomBounds.java +++ b/control/RandomBounds.java @@ -1,4 +1,4 @@ -// understandingcollections/RandomBounds.java +// control/RandomBounds.java // (c)2016 MindView LLC: see Copyright.txt // We make no guarantees that this code is fit for any purpose. // Visit http://mindviewinc.com/Books/OnJava/ for more book information. @@ -7,16 +7,9 @@ import onjava.*; public class RandomBounds { - static void usage() { - System.out.println("Usage:"); - System.out.println("\tRandomBounds lower"); - System.out.println("\tRandomBounds upper"); - System.exit(1); - } public static void main(String[] args) { - if(args.length != 1) usage(); new TimedAbort(3); - switch(args[0]) { + switch(args.length == 0 ? "" : args[0]) { case "lower": while(Math.random() != 0.0) ; // Keep trying @@ -28,7 +21,10 @@ public class RandomBounds { System.out.println("Produced 1.0!"); break; default: - usage(); + System.out.println("Usage:"); + System.out.println("\tRandomBounds lower"); + System.out.println("\tRandomBounds upper"); + System.exit(1); } } } diff --git a/understandingcollections/Lists.java b/understandingcollections/ListOps.java similarity index 98% rename from understandingcollections/Lists.java rename to understandingcollections/ListOps.java index d7ffe5a9..03de5f35 100644 --- a/understandingcollections/Lists.java +++ b/understandingcollections/ListOps.java @@ -1,4 +1,4 @@ -// understandingcollections/Lists.java +// understandingcollections/ListOps.java // (c)2016 MindView LLC: see Copyright.txt // We make no guarantees that this code is fit for any purpose. // Visit http://mindviewinc.com/Books/OnJava/ for more book information. @@ -6,7 +6,7 @@ import java.util.*; import onjava.*; -public class Lists { +public class ListOps { private static boolean b; private static String s; private static int i; diff --git a/understandingcollections/ListPerformance.java b/understandingcollections/ListPerformance.java deleted file mode 100644 index 2c0b1f99..00000000 --- a/understandingcollections/ListPerformance.java +++ /dev/null @@ -1,211 +0,0 @@ -// understandingcollections/ListPerformance.java -// (c)2016 MindView LLC: see Copyright.txt -// We make no guarantees that this code is fit for any purpose. -// Visit http://mindviewinc.com/Books/OnJava/ for more book information. -// Demonstrates performance differences in Lists -// Small to keep build testing short: -// {java ListPerformance 100 500} -import java.util.*; -import onjava.*; - -public class ListPerformance { - static SplittableRandom rand = new SplittableRandom(); - static int reps = 1000; - static List>> tests = - new ArrayList<>(); - static List>> qTests = - new ArrayList<>(); - static { - tests.add(new Test>("add") { - @Override - int test(List list, TestParam tp) { - int loops = tp.loops; - int listSize = tp.size; - for(int i = 0; i < loops; i++) { - list.clear(); - for(int j = 0; j < listSize; j++) - list.add(j); - } - return loops * listSize; - } - }); - tests.add(new Test>("get") { - @Override - int test(List list, TestParam tp) { - int loops = tp.loops * reps; - int listSize = list.size(); - for(int i = 0; i < loops; i++) - list.get(rand.nextInt(listSize)); - return loops; - } - }); - tests.add(new Test>("set") { - @Override - int test(List list, TestParam tp) { - int loops = tp.loops * reps; - int listSize = list.size(); - for(int i = 0; i < loops; i++) - list.set(rand.nextInt(listSize), 47); - return loops; - } - }); - tests.add(new Test>("iteradd") { - @Override - int test(List list, TestParam tp) { - final int LOOPS = 1000000; - int half = list.size() / 2; - ListIterator it = - list.listIterator(half); - for(int i = 0; i < LOOPS; i++) - it.add(47); - return LOOPS; - } - }); - tests.add(new Test>("insert") { - @Override - int test(List list, TestParam tp) { - int loops = tp.loops; - for(int i = 0; i < loops; i++) - list.add(5, 47); // Minimize random-access cost - return loops; - } - }); - tests.add(new Test>("remove") { - @Override - int test(List list, TestParam tp) { - int loops = tp.loops; - int size = tp.size; - for(int i = 0; i < loops; i++) { - list.clear(); - list.addAll(new CountingIntegerList(size)); - while(list.size() > 5) - list.remove(5); // Minimize random-access cost - } - return loops * size; - } - }); - // Tests for queue behavior: - qTests.add(new Test>("addFirst") { - @Override - int test(LinkedList list, TestParam tp) { - int loops = tp.loops; - int size = tp.size; - for(int i = 0; i < loops; i++) { - list.clear(); - for(int j = 0; j < size; j++) - list.addFirst(47); - } - return loops * size; - } - }); - qTests.add(new Test>("addLast") { - @Override - int test(LinkedList list, TestParam tp) { - int loops = tp.loops; - int size = tp.size; - for(int i = 0; i < loops; i++) { - list.clear(); - for(int j = 0; j < size; j++) - list.addLast(47); - } - return loops * size; - } - }); - qTests.add( - new Test>("rmFirst") { - @Override - int test(LinkedList list, TestParam tp) { - int loops = tp.loops; - int size = tp.size; - for(int i = 0; i < loops; i++) { - list.clear(); - list.addAll(new CountingIntegerList(size)); - while(list.size() > 0) - list.removeFirst(); - } - return loops * size; - } - }); - qTests.add(new Test>("rmLast") { - @Override - int test(LinkedList list, TestParam tp) { - int loops = tp.loops; - int size = tp.size; - for(int i = 0; i < loops; i++) { - list.clear(); - list.addAll(new CountingIntegerList(size)); - while(list.size() > 0) - list.removeLast(); - } - return loops * size; - } - }); - } - static class ListTester extends Tester> { - public ListTester(List collection, - List>> tests) { - super(collection, tests); - } - // Fill to the appropriate size before each test: - @Override - protected List initialize(int size) { - collection.clear(); - collection.addAll(new CountingIntegerList(size)); - return collection; - } - // Convenience method: - public static void run(List list, - List>> tests) { - new ListTester(list, tests).timedTest(); - } - } - public static void main(String[] args) { - if(args.length > 0) - Tester.defaultParams = TestParam.array(args); - // Can only do these two tests on an array: - Tester> arrayTest = - new Tester>(null, - tests.subList(1, 3)) { - // This is called before each test. It - // produces a non-resizeable array-backed list: - @Override protected - List initialize(int size) { - Integer[] ia = new Integer[size]; - Arrays.setAll(ia, new Count.Integer()::get); - return Arrays.asList(ia); - } - }; - arrayTest.setHeadline("Array as List"); - arrayTest.timedTest(); - Tester.defaultParams= TestParam.array( - 10, 5000, 100, 5000, 1000, 1000, 10000, 200); - if(args.length > 0) - Tester.defaultParams = TestParam.array(args); - ListTester.run(new ArrayList<>(), tests); - ListTester.run(new LinkedList<>(), tests); - ListTester.run(new Vector<>(), tests); - Tester.fieldWidth = 12; - Tester> qTest = - new Tester>( - new LinkedList<>(), qTests); - qTest.setHeadline("Queue tests"); - qTest.timedTest(); - } -} -/* Output: ---- Array as List --- - size get set - 100 50 68 ---------------------- ArrayList --------------------- - size add get set iteradd insert remove - 100 60 76 74 103 0 360 ---------------------- LinkedList --------------------- - size add get set iteradd insert remove - 100 200 166 114 201 0 120 ------------------------ Vector ----------------------- - size add get set iteradd insert remove - 100 60 76 84 159 0 220 --------------------- Queue tests -------------------- - size addFirst addLast rmFirst rmLast - 100 80 160 100 120 -*/ diff --git a/understandingcollections/Maps.java b/understandingcollections/MapOps.java similarity index 97% rename from understandingcollections/Maps.java rename to understandingcollections/MapOps.java index 005a609e..58b711cf 100644 --- a/understandingcollections/Maps.java +++ b/understandingcollections/MapOps.java @@ -1,4 +1,4 @@ -// understandingcollections/Maps.java +// understandingcollections/MapOps.java // (c)2016 MindView LLC: see Copyright.txt // We make no guarantees that this code is fit for any purpose. // Visit http://mindviewinc.com/Books/OnJava/ for more book information. @@ -7,7 +7,7 @@ import java.util.concurrent.*; import java.util.*; import onjava.*; -public class Maps { +public class MapOps { public static void printKeys(Map map) { System.out.print("Size = " + map.size() + ", "); System.out.print("Keys: "); diff --git a/understandingcollections/SetPerformance.java b/understandingcollections/SetPerformance.java deleted file mode 100644 index d354a97b..00000000 --- a/understandingcollections/SetPerformance.java +++ /dev/null @@ -1,70 +0,0 @@ -// understandingcollections/SetPerformance.java -// (c)2016 MindView LLC: see Copyright.txt -// We make no guarantees that this code is fit for any purpose. -// Visit http://mindviewinc.com/Books/OnJava/ for more book information. -// Demonstrates performance differences in Sets -// Small to keep build testing short: -// {java SetPerformance 100 5000} -import java.util.*; - -public class SetPerformance { - static List>> tests = - new ArrayList<>(); - static { - tests.add(new Test>("add") { - @Override - int test(Set set, TestParam tp) { - int loops = tp.loops; - int size = tp.size; - for(int i = 0; i < loops; i++) { - set.clear(); - for(int j = 0; j < size; j++) - set.add(j); - } - return loops * size; - } - }); - tests.add(new Test>("contains") { - @Override - int test(Set set, TestParam tp) { - int loops = tp.loops; - int span = tp.size * 2; - for(int i = 0; i < loops; i++) - for(int j = 0; j < span; j++) - set.contains(j); - return loops * span; - } - }); - tests.add(new Test>("iterate") { - @Override - int test(Set set, TestParam tp) { - int loops = tp.loops * 10; - for(int i = 0; i < loops; i++) { - Iterator it = set.iterator(); - while(it.hasNext()) - it.next(); - } - return loops * set.size(); - } - }); - } - public static void main(String[] args) { - if(args.length > 0) - Tester.defaultParams = TestParam.array(args); - Tester.fieldWidth = 10; - Tester.run(new TreeSet<>(), tests); - Tester.run(new HashSet<>(), tests); - Tester.run(new LinkedHashSet<>(), tests); - } -} -/* Output: -------------- TreeSet ------------- - size add contains iterate - 100 256 162 31 -------------- HashSet ------------- - size add contains iterate - 100 88 58 43 ----------- LinkedHashSet ---------- - size add contains iterate - 100 100 46 23 -*/ diff --git a/understandingcollections/Test.java b/understandingcollections/Test.java deleted file mode 100644 index d634cb13..00000000 --- a/understandingcollections/Test.java +++ /dev/null @@ -1,13 +0,0 @@ -// understandingcollections/Test.java -// (c)2016 MindView LLC: see Copyright.txt -// We make no guarantees that this code is fit for any purpose. -// Visit http://mindviewinc.com/Books/OnJava/ for more book information. -// Framework for performing timed tests of collections - -public abstract class Test { - String name; - public Test(String name) { this.name = name; } - // Override this method for different tests. - // Returns actual number of repetitions of test. - abstract int test(C collection, TestParam tp); -} diff --git a/understandingcollections/TestParam.java b/understandingcollections/TestParam.java deleted file mode 100644 index f7bc64bf..00000000 --- a/understandingcollections/TestParam.java +++ /dev/null @@ -1,30 +0,0 @@ -// understandingcollections/TestParam.java -// (c)2016 MindView LLC: see Copyright.txt -// We make no guarantees that this code is fit for any purpose. -// Visit http://mindviewinc.com/Books/OnJava/ for more book information. -// A "data transfer object." - -public class TestParam { - public final int size; - public final int loops; - public TestParam(int size, int loops) { - this.size = size; - this.loops = loops; - } - // Create an array of TestParam from a varargs sequence: - public static TestParam[] array(int... values) { - int size = values.length/2; - TestParam[] result = new TestParam[size]; - int n = 0; - for(int i = 0; i < size; i++) - result[i] = new TestParam(values[n++], values[n++]); - return result; - } - // Convert a String array to a TestParam array: - public static TestParam[] array(String[] values) { - int[] vals = new int[values.length]; - for(int i = 0; i < vals.length; i++) - vals[i] = Integer.decode(values[i]); - return array(vals); - } -} diff --git a/understandingcollections/Tester.java b/understandingcollections/Tester.java deleted file mode 100644 index 150f3bc7..00000000 --- a/understandingcollections/Tester.java +++ /dev/null @@ -1,84 +0,0 @@ -// understandingcollections/Tester.java -// (c)2016 MindView LLC: see Copyright.txt -// We make no guarantees that this code is fit for any purpose. -// Visit http://mindviewinc.com/Books/OnJava/ for more book information. -// Applies Test objects to lists of different collections -import java.util.*; -import java.time.*; - -public class Tester { - public static int fieldWidth = 8; - public static - TestParam[] defaultParams= TestParam.array( - 10, 5000, 100, 5000, 1000, 5000, 10000, 500); - // Override this to modify pre-test initialization: - protected C initialize(int size) { return collection; } - protected C collection; - private String headline = ""; - private List> tests; - private static int sizeWidth = 5; - private static String sizeField = "%" + sizeWidth + "s"; - private TestParam[] paramList = defaultParams; - public Tester(C collection, List> tests) { - this.collection = collection; - this.tests = tests; - if(collection != null) - headline = collection.getClass().getSimpleName(); - } - public Tester(C collection, List> tests, - TestParam[] paramList) { - this(collection, tests); - this.paramList = paramList; - } - public void setHeadline(String newHeadline) { - headline = newHeadline; - } - // Generic methods for convenience : - public static - void run(C cntnr, List> tests) { - new Tester<>(cntnr, tests).timedTest(); - } - public static void run(C cntnr, - List> tests, TestParam[] paramList) { - new Tester<>(cntnr, tests, paramList).timedTest(); - } - private void displayHeader() { - // Calculate width and pad with '-': - int width = fieldWidth * tests.size() + sizeWidth; - int dashLength = width - headline.length() - 1; - StringBuilder head = new StringBuilder(width); - for(int i = 0; i < dashLength/2; i++) - head.append('-'); - head.append(' '); - head.append(headline); - head.append(' '); - for(int i = 0; i < dashLength/2; i++) - head.append('-'); - System.out.println(head); - // Print column headers: - System.out.format(sizeField, "size"); - for(Test test : tests) - System.out.format( - "%" + fieldWidth + "s", test.name); - System.out.println(); - } - // Run the tests for this collection: - public void timedTest() { - displayHeader(); - for(TestParam param : paramList) { - System.out.format(sizeField, param.size); - for(Test test : tests) { - C kontainer = initialize(param.size); - Instant start = Instant.now(); - // Call the overriden method: - int reps = test.test(kontainer, param); - Duration elapsed = - Duration.between(start, Instant.now()); - Duration timePerRep = elapsed.dividedBy(reps); - System.out.format("%" + fieldWidth + "d", - timePerRep.toNanos()); - } - System.out.println(); - } - } -} diff --git a/understandingcollections/jmhtests/Lists.java b/understandingcollections/jmhtests/Lists.java new file mode 100644 index 00000000..296164dc --- /dev/null +++ b/understandingcollections/jmhtests/Lists.java @@ -0,0 +1,81 @@ +// understandingcollections/jmhtests/Lists.java +// (c)2016 MindView LLC: see Copyright.txt +// We make no guarantees that this code is fit for any purpose. +// Visit http://mindviewinc.com/Books/OnJava/ for more book information. +// Demonstrates performance differences in Lists +package understandingcollections.jmhtests; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import java.util.*; +import static java.util.concurrent.TimeUnit.*; + +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1, timeUnit = SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = SECONDS) +@Fork(1) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(MICROSECONDS) +public class Lists { + private List list; + + @Param({"ArrayList", "LinkedList", "Vector"}) + private String type; + + private int begin; + private int end; + + @Setup + public void setup() { + switch(type) { + case "ArrayList": + list = new ArrayList<>(); + break; + case "LinkedList": + list = new LinkedList<>(); + break; + case "Vector": + list = new Vector<>(); + break; + default: + throw new IllegalStateException("Unknown " + type); + } + begin = 0; + end = 256; + for(int i = begin; i < end; i++) { + list.add(i); + } + } + @Benchmark + public void add() { + for(int i = begin; i < end; i++) + list.add(i); + } + @Benchmark + public void get(Blackhole bh) { + for(int i = begin; i < end; i++) + bh.consume(list.get(i)); + } + @Benchmark + public void set() { + for(int i = begin; i < end; i++) + list.set(i, 47); + } + @Benchmark + public void iteradd() { + int half = list.size() / 2; + ListIterator it = + list.listIterator(half); + for(int i = begin; i < end; i++) + it.add(47); + } + @Benchmark + public void insert() { + for(int i = begin; i < end; i++) + list.add(5, 47); + } + @Benchmark + public void remove() { + while(list.size() > 5) + list.remove(5); + } +} diff --git a/understandingcollections/jmhtests/Maps.java b/understandingcollections/jmhtests/Maps.java index 76ca06ec..9a9022b6 100644 --- a/understandingcollections/jmhtests/Maps.java +++ b/understandingcollections/jmhtests/Maps.java @@ -14,7 +14,7 @@ import static java.util.concurrent.TimeUnit.*; @Measurement(iterations = 5, time = 1, timeUnit = SECONDS) @Fork(1) @BenchmarkMode(Mode.AverageTime) -@OutputTimeUnit(NANOSECONDS) +@OutputTimeUnit(MICROSECONDS) public class Maps { private Map map; @@ -49,33 +49,28 @@ public class Maps { default: throw new IllegalStateException("Unknown " + type); } - begin = 1; end = 256; for (int i = begin; i < end; i++) { map.put(i, i); } } - @Benchmark public void get(Blackhole bh) { for (int i = begin; i < end; i++) { bh.consume(map.get(i)); } } - @Benchmark public void put() { for (int i = begin; i < end; i++) { map.put(i, i); } } - @Benchmark public void iterate(Blackhole bh) { Iterator it = map.entrySet().iterator(); while(it.hasNext()) bh.consume(it.next()); } - } diff --git a/understandingcollections/jmhtests/Queues.java b/understandingcollections/jmhtests/Queues.java new file mode 100644 index 00000000..1428b07a --- /dev/null +++ b/understandingcollections/jmhtests/Queues.java @@ -0,0 +1,62 @@ +// understandingcollections/jmhtests/Queues.java +// (c)2016 MindView LLC: see Copyright.txt +// We make no guarantees that this code is fit for any purpose. +// Visit http://mindviewinc.com/Books/OnJava/ for more book information. +// Demonstrates performance differences in Queues +package understandingcollections.jmhtests; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import java.util.*; +import static java.util.concurrent.TimeUnit.*; + +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1, timeUnit = SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = SECONDS) +@Fork(1) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(MICROSECONDS) +public class Queues { + private LinkedList queue; + + @Param({"LinkedList"}) + private String type; + + private int begin; + private int end; + + @Setup + public void setup() { + switch(type) { + case "LinkedList": + queue = new LinkedList<>(); + break; + default: + throw new IllegalStateException("Unknown " + type); + } + begin = 1; + end = 256; + for(int i = begin; i < end; i++) { + queue.add(i); + } + } + @Benchmark + public void queue_addFirst() { + for(int i = begin; i < end; i++) + queue.addFirst(47); + } + @Benchmark + public void queue_addLast() { + for(int i = begin; i < end; i++) + queue.addLast(47); + } + @Benchmark + public void queue_removeFirst(Blackhole bh) { + while(queue.size() > 0) + bh.consume(queue.removeFirst()); + } + @Benchmark + public void queue_removeLast(Blackhole bh) { + while(queue.size() > 0) + bh.consume(queue.removeLast()); + } +} diff --git a/understandingcollections/jmhtests/Sets.java b/understandingcollections/jmhtests/Sets.java new file mode 100644 index 00000000..3cb94322 --- /dev/null +++ b/understandingcollections/jmhtests/Sets.java @@ -0,0 +1,63 @@ +// understandingcollections/jmhtests/Sets.java +// (c)2016 MindView LLC: see Copyright.txt +// We make no guarantees that this code is fit for any purpose. +// Visit http://mindviewinc.com/Books/OnJava/ for more book information. +// Demonstrates performance differences in Sets +package understandingcollections.jmhtests; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.infra.Blackhole; +import java.util.*; +import static java.util.concurrent.TimeUnit.*; + +@State(Scope.Thread) +@Warmup(iterations = 5, time = 1, timeUnit = SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = SECONDS) +@Fork(1) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(MICROSECONDS) +public class Sets { + private Set set; + + @Param({"HashSet", "TreeSet", "LinkedHashSet"}) + private String type; + + private int begin; + private int end; + + @Setup + public void setup() { + switch(type) { + case "HashSet": + set = new HashSet<>(); + break; + case "TreeSet": + set = new TreeSet<>(); + break; + case "LinkedHashSet": + set = new LinkedHashSet<>(); + break; + default: + throw new IllegalStateException("Unknown " + type); + } + begin = 1; + end = 256; + for (int i = begin; i < end; i++) + set.add(i); + } + @Benchmark + public void add() { + for(int i = begin; i < end; i++) + set.add(i); + } + @Benchmark + public void contains(Blackhole bh) { + for(int i = begin; i < end; i++) + bh.consume(set.contains(i)); + } + @Benchmark + public void iterate(Blackhole bh) { + Iterator it = set.iterator(); + while(it.hasNext()) + bh.consume(it.next()); + } +}