From 64bfbbbd58a7c412862a558d5bcc2d1aca9c9895 Mon Sep 17 00:00:00 2001 From: Morten Haraldsen Date: Sun, 24 Mar 2024 20:46:01 +0100 Subject: [PATCH] v1.4.0 --- pom.xml | 16 +++---- .../java/com/ethlo/time/ChronographData.java | 48 ++++++++++++++----- .../DurationPerformanceStatistics.java | 5 ++ .../ThroughputPerformanceStatistics.java | 5 ++ .../util/IndexedCollectionStatistics.java | 8 ++++ src/main/java/com/ethlo/util/LongList.java | 4 ++ src/main/java/com/ethlo/util/MathUtil.java | 18 ++++--- .../java/com/ethlo/time/ChronographTest.java | 22 +++++++++ .../com/ethlo/util/ListPerformanceTest.java | 18 +++---- 9 files changed, 110 insertions(+), 34 deletions(-) diff --git a/pom.xml b/pom.xml index a6ce3ca..1b8f9ba 100755 --- a/pom.xml +++ b/pom.xml @@ -6,8 +6,8 @@ com.ethlo.time chronograph Chronograph - Easy to use timer allowing accurate measurement of elapsed time - 1.3.0-SNAPSHOT + Easy-to-use stopwatch for task performance statistics + 1.4.0 Apache License, Version 2.0 @@ -36,7 +36,7 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + 0.8.11 prepare-agent @@ -98,7 +98,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.8 + 1.6.13 true sonatype-nexus-staging @@ -109,7 +109,7 @@ org.apache.maven.plugins maven-gpg-plugin - 3.0.1 + 1.6 sign-artifacts @@ -117,11 +117,11 @@ sign + + gpg + - - false - org.codehaus.mojo diff --git a/src/main/java/com/ethlo/time/ChronographData.java b/src/main/java/com/ethlo/time/ChronographData.java index 86abe0a..53ac061 100644 --- a/src/main/java/com/ethlo/time/ChronographData.java +++ b/src/main/java/com/ethlo/time/ChronographData.java @@ -21,8 +21,13 @@ */ import java.time.Duration; -import java.util.LinkedList; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; + +import com.ethlo.time.statistics.DurationPerformanceStatistics; +import com.ethlo.time.statistics.ThroughputPerformanceStatistics; public class ChronographData { @@ -39,20 +44,23 @@ public ChronographData(final String name, final List public static ChronographData combine(final String name, final List toCombine) { - final List all = new LinkedList<>(); - for (Chronograph c : toCombine) + if (toCombine.isEmpty()) { - final ChronographData taskData = c.getTaskData(); - final String runName = taskData.name; - - for (TaskPerformanceStatistics taskStats : taskData.taskStatistics) + throw new IllegalArgumentException("No results to combine"); + } + if (toCombine.size() == 1) + { + return toCombine.get(0).getTaskData(); + } + else + { + ChronographData last = toCombine.get(0).getTaskData(); + for (int i = 1; i < toCombine.size(); i++) { - final String fullName = runName != null && runName.length() > 0 ? (runName + " - " + taskStats.getName()) : taskStats.getName(); - all.add(new TaskPerformanceStatistics(fullName, taskStats.getSampleSize(), taskStats.getDurationStatistics(), taskStats.getThroughputStatistics())); + last = last.merge(name, toCombine.get(i).getTaskData()); } + return last; } - final Duration total = toCombine.stream().map(Chronograph::getTaskData).map(ChronographData::getTotalTime).reduce(Duration.ZERO, Duration::plus); - return new ChronographData(name, all, total); } public String getName() @@ -74,4 +82,22 @@ public boolean isEmpty() { return this.taskStatistics.isEmpty(); } + + public ChronographData merge(String chronographName, ChronographData chronographData) + { + final Map joined = new LinkedHashMap<>(Math.max(this.taskStatistics.size(), chronographData.taskStatistics.size())); + this.taskStatistics.forEach(t -> joined.put(t.getName(), t)); + chronographData.taskStatistics.forEach(t -> joined.compute(t.getName(), (k, v) -> + { + if (v != null) + { + final long sampleSize = v.getSampleSize() + t.getSampleSize(); + final DurationPerformanceStatistics durationStatistics = ((DurationPerformanceStatistics) t.getDurationStatistics()).merge((DurationPerformanceStatistics) v.getDurationStatistics()); + final ThroughputPerformanceStatistics throughputStatistics = ((ThroughputPerformanceStatistics) t.getThroughputStatistics()).merge((ThroughputPerformanceStatistics) v.getThroughputStatistics()); + return new TaskPerformanceStatistics(k, sampleSize, durationStatistics, throughputStatistics); + } + return t; + })); + return new ChronographData(chronographName, new ArrayList<>(joined.values()), Duration.ofNanos(joined.values().stream().mapToLong(t -> t.getDurationStatistics().getElapsedTotal().toNanos()).sum())); + } } diff --git a/src/main/java/com/ethlo/time/statistics/DurationPerformanceStatistics.java b/src/main/java/com/ethlo/time/statistics/DurationPerformanceStatistics.java index 6d7fdfb..50201a2 100644 --- a/src/main/java/com/ethlo/time/statistics/DurationPerformanceStatistics.java +++ b/src/main/java/com/ethlo/time/statistics/DurationPerformanceStatistics.java @@ -84,4 +84,9 @@ public Duration getStandardDeviation() } return Duration.ofNanos(MathUtil.sqrt(sd).longValue()); } + + public DurationPerformanceStatistics merge(final DurationPerformanceStatistics other) + { + return new DurationPerformanceStatistics(this.collectionStatistics.merge(other.collectionStatistics), totalInvocations + other.totalInvocations, elapsedTotal.plus(other.elapsedTotal)); + } } diff --git a/src/main/java/com/ethlo/time/statistics/ThroughputPerformanceStatistics.java b/src/main/java/com/ethlo/time/statistics/ThroughputPerformanceStatistics.java index d123ada..63210cf 100644 --- a/src/main/java/com/ethlo/time/statistics/ThroughputPerformanceStatistics.java +++ b/src/main/java/com/ethlo/time/statistics/ThroughputPerformanceStatistics.java @@ -96,4 +96,9 @@ public Double getStandardDeviation() } return MathUtil.sqrt(sd).doubleValue(); } + + public ThroughputPerformanceStatistics merge(final ThroughputPerformanceStatistics other) + { + return new ThroughputPerformanceStatistics(this.collectionStatistics.merge(other.collectionStatistics), totalInvocations + other.totalInvocations, elapsedTotal.plus(other.elapsedTotal)); + } } diff --git a/src/main/java/com/ethlo/util/IndexedCollectionStatistics.java b/src/main/java/com/ethlo/util/IndexedCollectionStatistics.java index 48e7b22..4d8cab5 100644 --- a/src/main/java/com/ethlo/util/IndexedCollectionStatistics.java +++ b/src/main/java/com/ethlo/util/IndexedCollectionStatistics.java @@ -96,4 +96,12 @@ public IndexedCollection getList() { return list; } + + public IndexedCollectionStatistics merge(IndexedCollectionStatistics other) + { + final LongList list = new LongList((Math.max(10, this.list.size() + other.list.size()) / 10)); + this.list.forEach(list::add); + other.list.forEach(list::add); + return new IndexedCollectionStatistics(list); + } } diff --git a/src/main/java/com/ethlo/util/LongList.java b/src/main/java/com/ethlo/util/LongList.java index 0d98d1a..857209f 100644 --- a/src/main/java/com/ethlo/util/LongList.java +++ b/src/main/java/com/ethlo/util/LongList.java @@ -44,6 +44,10 @@ public LongList() public LongList(int blockSize) { + if (blockSize < 1) + { + throw new IllegalArgumentException("blockSize cannot be less than 1"); + } this.blockSize = blockSize; } diff --git a/src/main/java/com/ethlo/util/MathUtil.java b/src/main/java/com/ethlo/util/MathUtil.java index afb6e1a..39722fa 100644 --- a/src/main/java/com/ethlo/util/MathUtil.java +++ b/src/main/java/com/ethlo/util/MathUtil.java @@ -20,12 +20,13 @@ * #L% */ -import static java.math.BigDecimal.ROUND_HALF_UP; - import java.math.BigDecimal; +import java.math.RoundingMode; public class MathUtil { + private static final BigDecimal TWO = BigDecimal.valueOf(2); + public static BigDecimal sqrt(BigDecimal value) { return sqrt(value, 10); @@ -33,16 +34,19 @@ public static BigDecimal sqrt(BigDecimal value) public static BigDecimal sqrt(BigDecimal value, final int SCALE) { - BigDecimal TWO = BigDecimal.valueOf(2); + if (value.compareTo(BigDecimal.ZERO) == 0) + { + return value; + } + BigDecimal x0 = BigDecimal.ZERO; - BigDecimal x1 = new BigDecimal(Math.sqrt(value.doubleValue())); + BigDecimal x1 = BigDecimal.valueOf(Math.sqrt(value.doubleValue())); while (!x0.equals(x1)) { x0 = x1; - x1 = value.divide(x0, SCALE, ROUND_HALF_UP); + x1 = value.divide(x0, SCALE, RoundingMode.HALF_UP); x1 = x1.add(x0); - x1 = x1.divide(TWO, SCALE, ROUND_HALF_UP); - + x1 = x1.divide(TWO, SCALE, RoundingMode.HALF_UP); } return x1; } diff --git a/src/test/java/com/ethlo/time/ChronographTest.java b/src/test/java/com/ethlo/time/ChronographTest.java index 88479d7..04993be 100644 --- a/src/test/java/com/ethlo/time/ChronographTest.java +++ b/src/test/java/com/ethlo/time/ChronographTest.java @@ -29,6 +29,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.ethlo.ascii.TableTheme; import com.ethlo.util.SleepUtil; public class ChronographTest @@ -242,4 +243,25 @@ public void testGetAverageForNonStoppedTask() chronograph.start(taskName); assertThat(chronograph.getTasks(taskName).getDurationStatistics().getAverage()).isEqualTo(Duration.ZERO); } + + @Test + void testMergeResults() + { + final Chronograph chronograph1 = Chronograph.create(); + chronograph1.start(taskName); + + final Chronograph chronograph2 = Chronograph.create(); + chronograph2.start(taskName); + + chronograph1.stop(); + chronograph2.stop(); + final ChronographData merged = chronograph2.getTaskData().merge("merged", chronograph1.getTaskData()); + + System.out.println(Report.prettyPrint(merged, + OutputConfig.EXTENDED.mode(PresentationMode.THROUGHPUT).benchmarkMode(true), + TableTheme.RED_HERRING + )); + + assertThat(true).isTrue(); + } } diff --git a/src/test/java/com/ethlo/util/ListPerformanceTest.java b/src/test/java/com/ethlo/util/ListPerformanceTest.java index 6a4a334..4468f61 100644 --- a/src/test/java/com/ethlo/util/ListPerformanceTest.java +++ b/src/test/java/com/ethlo/util/ListPerformanceTest.java @@ -124,9 +124,9 @@ private Chronograph performAddBenchmark(final int runs, final int size) for (int i = 0; i < runs; i++) { - c.timedFunction("LinkedList", this::addLinkedList, size); - c.timedFunction("ArrayList", this::addArrayList, size); - c.timedFunction("IndexedCollection", this::addLongList, size); + c.timedFunction("LinkedList - Add", this::addLinkedList, size); + c.timedFunction("ArrayList - Add", this::addArrayList, size); + c.timedFunction("IndexedCollection - Add", this::addLongList, size); } return c; } @@ -154,9 +154,9 @@ private Chronograph performSortBenchmark(final int runs, final int size) final List linkedList = addLinkedList(size); final List arrayList = addArrayList(size); - c.timed("LinkedList", () -> linkedList.sort(Comparator.naturalOrder())); - c.timed("Arraylist", () -> arrayList.sort(Comparator.naturalOrder())); - c.timed("IndexedCollection", longList::sort); + c.timed("LinkedList - Sort", () -> linkedList.sort(Comparator.naturalOrder())); + c.timed("ArrayList - Sort", () -> arrayList.sort(Comparator.naturalOrder())); + c.timed("IndexedCollection - Sort", longList::sort); } return c; } @@ -165,8 +165,10 @@ private Chronograph performSortBenchmark(final int runs, final int size) void testCombinedPerformanceTable() { final Chronograph a = performAddBenchmark(20, 10_000); - final Chronograph b = performSortBenchmark(10, 10_000); - final ChronographData combined = ChronographData.combine("Combined", Arrays.asList(a, b)); + final Chronograph b = performAddBenchmark(10, 10_000); + final Chronograph c = performAddBenchmark(5, 10_000); + final Chronograph d = performSortBenchmark(8, 10_000); + final ChronographData combined = ChronographData.combine("Combined", Arrays.asList(a, b, c, d)); System.out.println(Report.prettyPrint(combined, OutputConfig.EXTENDED.mode(PresentationMode.THROUGHPUT).benchmarkMode(true),