From the previous post, I have incorporated the nice answers made by Chris and Chip01.
Code
package io.github.coderodde.statistics.run;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
/**
* This record class encapsulates the run statistics.
*
* @author Rodion "rodde" Efremov
*/
public final record RunStatistics(long minimumDuration,
long maximumDuration,
double meanDuration,
double medianDuration,
double standardDeviation) {
@Override
public String toString() {
// NumberFormat.format() is not thread-safe, so instantiate and call
// here:
final NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
final DecimalFormatSymbols symbols = new DecimalFormatSymbols();
symbols.setDecimalSeparator('.');
symbols.setGroupingSeparator(',');
final DecimalFormat df = new DecimalFormat("0.###", symbols);
return new StringBuilder("min = ")
.append(nf.format(minimumDuration))
.append(" ns, max = ")
.append(nf.format(maximumDuration))
.append(" ns, mean = ")
.append(df.format(meanDuration))
.append(" ns, median = ")
.append(df.format(medianDuration))
.append(" ns, sd = ")
.append(df.format(standardDeviation))
.append(" ns")
.toString();
}
}
package io.github.coderodde.statistics.run;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* This class provides methods for obtaining the running time statistics.
*
* @author Rodion "rodde" Efremov
*/
public final class Runner {
private static final int MINIMUM_ITERATIONS = 5;
public static RunStatistics measure(final Runnable runnable,
final int iterations) {
Objects.requireNonNull(runnable, "The input runnable is null");
checkIterations(iterations);
double meanDuration = 0.0;
double medianDuration;
double standardDeviation;
final List<Long> durations = new ArrayList<>(iterations);
for (int iteration = 0;
iteration < iterations;
iteration++) {
final long duration = measure(runnable);
meanDuration += duration;
durations.add(duration);
}
Collections.sort(durations);
final long minimumDuration = durations.getFirst();
final long maximumDuration = durations.getLast();
meanDuration /= iterations;
medianDuration = computeMedianDuration(durations);
standardDeviation = computeStandardDeviation(durations, meanDuration);
return new RunStatistics(minimumDuration,
maximumDuration,
meanDuration,
medianDuration,
standardDeviation);
}
public static RunStatistics measure(final List<Runnable> runnables) {
Objects.requireNonNull(runnables, "The input runnables is null");
if (runnables.isEmpty()) {
throw new IllegalArgumentException("Nothing to measure");
}
double meanDuration = 0.0;
double medianDuration;
double standardDeviation;
final List<Long> durations = new ArrayList<>(runnables.size());
for (final Runnable runnable : runnables) {
final long duration = measure(runnable);
meanDuration += duration;
durations.add(duration);
}
Collections.sort(durations);
long minimumDuration = durations.getFirst();
long maximumDuration = durations.getLast();
meanDuration /= runnables.size();
medianDuration = computeMedianDuration(durations);
standardDeviation = computeStandardDeviation(durations, meanDuration);
return new RunStatistics(minimumDuration,
maximumDuration,
meanDuration,
medianDuration,
standardDeviation);
}
private static long measure(final Runnable runnable) {
final long ta = System.nanoTime();
runnable.run();
final long tb = System.nanoTime();
final long duration = tb - ta;
return duration;
}
private static double computeMedianDuration(final List<Long> durations) {
if (durations.size() % 2 == 1) {
return durations.get(durations.size() / 2);
} else {
final int index1 = durations.size() / 2;
final int index2 = index1 - 1;
return (durations.get(index1) +
durations.get(index2)) / 2.0;
}
}
private static double computeStandardDeviation(final List<Long> durations,
final double meanDuration) {
double sum = 0.0;
for (final Long duration : durations) {
sum += Math.pow(duration - meanDuration, 2.0);
}
return Math.sqrt(sum / durations.size());
}
private static void checkIterations(final int iterations) {
if (iterations < MINIMUM_ITERATIONS) {
final String exceptionMessage =
String.format("Number of iterations (%d) is too small. " +
"Must be at least %d.",
iterations,
MINIMUM_ITERATIONS);
throw new IllegalArgumentException(exceptionMessage);
}
}
}
package io.github.coderodde.statistics.run.demo;
import io.github.coderodde.statistics.run.RunStatistics;
import io.github.coderodde.statistics.run.Runner;
import java.util.Random;
public class Demo {
private static final Random RANDOM = new Random(13L);
public static void main(String[] args) {
final RunStatistics runStatistics = Runner.measure(() -> {
try {
Thread.sleep(RANDOM.nextInt(100));
} catch (InterruptedException ex) {}
}, 20);
System.out.println(runStatistics);
}
}
Typical output
min = 18,500 ns, max = 98,307,500 ns, mean = 48755625 ns, median = 56061450 ns, sd = 30235412.72 ns
Critique request
Please, tell me whether I am getting anywhere.