changeset 375:66fdf41496ea

Print run progress and ETA estimates.
author shade
date Mon, 27 Jan 2014 18:38:20 +0400
parents 312f23be19ac
children 61a59c1c9453
files jmh-core/src/main/java/org/openjdk/jmh/runner/ActionPlan.java jmh-core/src/main/java/org/openjdk/jmh/runner/ActionType.java jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkRecord.java jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java jmh-core/src/main/java/org/openjdk/jmh/runner/parameters/TimeValue.java
diffstat 6 files changed, 170 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/ActionPlan.java	Sun Jan 26 13:26:06 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/ActionPlan.java	Mon Jan 27 18:38:20 2014 +0400
@@ -32,11 +32,17 @@
 public class ActionPlan implements Serializable {
 
     private final List<Action> actions;
+    private final ActionType type;
 
-    public ActionPlan() {
+    public ActionPlan(ActionType type) {
+        this.type = type;
         actions = new ArrayList<Action>();
     }
 
+    public ActionType getType() {
+        return type;
+    }
+
     void addWarmup(BenchmarkRecord record) {
         actions.add(new Action(record, ActionMode.WARMUP));
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/ActionType.java	Mon Jan 27 18:38:20 2014 +0400
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.runner;
+
+public enum ActionType {
+    EMBEDDED,
+    FORKED,
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java	Sun Jan 26 13:26:06 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/BaseRunner.java	Mon Jan 27 18:38:20 2014 +0400
@@ -40,6 +40,7 @@
 import java.lang.management.ManagementFactory;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
@@ -51,6 +52,11 @@
 
 public abstract class BaseRunner {
 
+    private long projectedTotalTime;
+    private long projectedRunningTime;
+    private long actualRunningTime;
+    private long benchmarkStart;
+
     /** Class holding all our runtime options/arguments */
     protected final Options options;
 
@@ -70,6 +76,7 @@
             ActionMode mode = action.getMode();
 
             if (!forked) {
+                beforeBenchmark();
                 out.println("# Fork: N/A, test runs in the existing VM");
             }
 
@@ -88,11 +95,63 @@
                 default:
                     throw new IllegalStateException("Unknown mode: " + mode);
             }
+
+            if (!forked) {
+                afterBenchmark(benchmark);
+            }
         }
 
         return results;
     }
 
+    protected void afterBenchmark(BenchmarkRecord name) {
+        long current = System.nanoTime();
+        projectedRunningTime += name.estimatedTimeSingleFork(options);
+        actualRunningTime += (current - benchmarkStart);
+        benchmarkStart = current;
+    }
+
+    protected void beforeBenchmarks(Collection<ActionPlan> plans) {
+        projectedTotalTime = 0;
+        for (ActionPlan plan : plans) {
+            for (Action act : plan.getActions()) {
+                projectedTotalTime += act.getBenchmark().estimatedTime(options);
+            }
+        }
+    }
+
+    protected void beforeBenchmark() {
+        if (benchmarkStart == 0) {
+            benchmarkStart = System.nanoTime();
+        }
+
+        long totalETA;
+        double partsDone = 1.0D * projectedRunningTime / projectedTotalTime;
+        if (partsDone != 0) {
+            totalETA = (long) (actualRunningTime * (1.0D / partsDone - 1));
+        } else {
+            totalETA = projectedTotalTime;
+        }
+
+        out.println(String.format("# Run progress: %.2f%% complete, ETA %s", partsDone * 100, formatDuration(totalETA)));
+    }
+
+    private String formatDuration(long nanos) {
+        long days = TimeUnit.NANOSECONDS.toDays(nanos);
+        nanos -= days * TimeUnit.DAYS.toNanos(1);
+
+        long hrs = TimeUnit.NANOSECONDS.toHours(nanos);
+        nanos -= hrs * TimeUnit.HOURS.toNanos(1);
+
+        long mins = TimeUnit.NANOSECONDS.toMinutes(nanos);
+        nanos -= mins * TimeUnit.MINUTES.toNanos(1);
+
+        long secs = TimeUnit.NANOSECONDS.toSeconds(nanos);
+        nanos -= secs * TimeUnit.SECONDS.toNanos(1);
+
+        return String.format("%s%02d:%02d:%02d", (days > 0) ? days + "days, " : "", hrs, mins, secs);
+    }
+
     BenchResult runBenchmark(BenchmarkRecord benchmark, ActionMode mode) {
         MicroBenchmarkHandler handler = null;
         try {
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkRecord.java	Sun Jan 26 13:26:06 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/BenchmarkRecord.java	Mon Jan 27 18:38:20 2014 +0400
@@ -25,11 +25,14 @@
 package org.openjdk.jmh.runner;
 
 import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.parameters.Defaults;
 import org.openjdk.jmh.runner.parameters.TimeValue;
 import org.openjdk.jmh.util.internal.Optional;
 
 import java.io.Serializable;
 import java.util.Collection;
+import java.util.concurrent.TimeUnit;
 
 public class BenchmarkRecord implements Comparable<BenchmarkRecord>, Serializable {
 
@@ -241,4 +244,37 @@
     public Optional<Integer> getThreads() {
         return threads;
     }
+
+    public long estimatedTimeSingleFork(Options opts) {
+        int mi = opts.getMeasurementIterations()
+                .orElse(getMeasurementIterations()
+                        .orElse(Defaults.MEASUREMENT_ITERATIONS));
+
+        TimeValue mt = opts.getMeasurementTime()
+                .orElse(getMeasurementTime()
+                        .orElse(Defaults.ITERATION_TIME));
+
+        int wi = opts.getWarmupIterations()
+                .orElse(getWarmupIterations()
+                        .orElse(Defaults.WARMUP_ITERATIONS));
+
+        TimeValue wt = opts.getWarmupTime()
+                .orElse(getWarmupTime()
+                        .orElse(Defaults.WARMUP_TIME));
+
+        return (wi * wt.getTime(TimeUnit.NANOSECONDS) + mi * mt.getTime(TimeUnit.NANOSECONDS));
+    }
+
+    public long estimatedTime(Options opts) {
+        int forks = opts.getForkCount()
+                .orElse(getForks()
+                        .orElse(Defaults.FORKS));
+
+        int warmupForks = opts.getWarmupForkCount()
+                .orElse(getWarmupForks()
+                        .orElse(Defaults.WARMUP_FORKS));
+
+        return (Math.max(1, forks) + warmupForks) * estimatedTimeSingleFork(opts);
+    }
+
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Sun Jan 26 13:26:06 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/Runner.java	Mon Jan 27 18:38:20 2014 +0400
@@ -49,7 +49,6 @@
 import java.io.IOException;
 import java.io.PrintStream;
 import java.lang.management.ManagementFactory;
-import java.lang.management.RuntimeMXBean;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -57,7 +56,6 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.SortedSet;
@@ -206,39 +204,8 @@
         return results;
     }
 
-    private ActionPlan getEmbeddedActionPlan(SortedSet<BenchmarkRecord> benchmarks) {
-        ActionPlan r = new ActionPlan();
-
-        LinkedHashSet<BenchmarkRecord> warmupBenches = new LinkedHashSet<BenchmarkRecord>();
-
-        List<String> warmupMicrosRegexp = options.getWarmupIncludes();
-        if (warmupMicrosRegexp != null && !warmupMicrosRegexp.isEmpty()) {
-            warmupBenches.addAll(list.find(out, warmupMicrosRegexp, Collections.<String>emptyList()));
-        }
-        if (options.getWarmupMode().orElse(Defaults.WARMUP_MODE).isBulk()) {
-            warmupBenches.addAll(benchmarks);
-        }
-
-        for (BenchmarkRecord wr : warmupBenches) {
-            r.addWarmup(wr);
-        }
-
-        for (BenchmarkRecord br : benchmarks) {
-            BenchmarkParams params = new BenchmarkParams(options, br, ActionMode.UNDEF);
-            if (params.getForks() <= 0) {
-                if (options.getWarmupMode().orElse(Defaults.WARMUP_MODE).isIndi()) {
-                    r.addWarmupMeasurement(br);
-                } else {
-                    r.addMeasurement(br);
-                }
-            }
-        }
-
-        return r;
-    }
-
-    private List<ActionPlan> getForkedActionPlans(Set<BenchmarkRecord> benchmarks) {
-        ActionPlan base = new ActionPlan();
+    private List<ActionPlan> getActionPlans(Set<BenchmarkRecord> benchmarks) {
+        ActionPlan base = new ActionPlan(ActionType.FORKED);
 
         LinkedHashSet<BenchmarkRecord> warmupBenches = new LinkedHashSet<BenchmarkRecord>();
 
@@ -254,11 +221,23 @@
             base.addWarmup(wr);
         }
 
+        ActionPlan embeddedPlan = new ActionPlan(ActionType.EMBEDDED);
+        embeddedPlan.mixIn(base);
+
         List<ActionPlan> result = new ArrayList<ActionPlan>();
         for (BenchmarkRecord br : benchmarks) {
             BenchmarkParams params = new BenchmarkParams(options, br, ActionMode.UNDEF);
+
+            if (params.getForks() <= 0) {
+                if (options.getWarmupMode().orElse(Defaults.WARMUP_MODE).isIndi()) {
+                    embeddedPlan.addWarmupMeasurement(br);
+                } else {
+                    embeddedPlan.addMeasurement(br);
+                }
+            }
+
             if (params.getForks() > 0) {
-                ActionPlan r = new ActionPlan();
+                ActionPlan r = new ActionPlan(ActionType.FORKED);
                 r.mixIn(base);
                 if (options.getWarmupMode().orElse(Defaults.WARMUP_MODE).isIndi()) {
                     r.addWarmupMeasurement(br);
@@ -268,6 +247,7 @@
                 result.add(r);
             }
         }
+        result.add(embeddedPlan);
 
         return result;
     }
@@ -276,17 +256,23 @@
         out.startRun();
 
         Multimap<BenchmarkRecord, BenchResult> results = new TreeMultimap<BenchmarkRecord, BenchResult>();
+        List<ActionPlan> plan = getActionPlans(benchmarks);
 
-        {
-            ActionPlan actionPlan = getEmbeddedActionPlan(benchmarks);
-            Multimap<BenchmarkRecord, BenchResult> res = runBenchmarks(false, actionPlan);
-            for (BenchmarkRecord br : res.keys()) {
-                results.putAll(br, res.get(br));
+        beforeBenchmarks(plan);
+
+        for (ActionPlan r : plan) {
+            Multimap<BenchmarkRecord, BenchResult> res;
+            switch (r.getType()) {
+                case EMBEDDED:
+                    res = runBenchmarks(false, r);
+                    break;
+                case FORKED:
+                    res = runSeparate(r);
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown action plan type: " + r.getType());
             }
-        }
 
-        for (ActionPlan r : getForkedActionPlans(benchmarks)) {
-            Multimap<BenchmarkRecord, BenchResult> res = runSeparate(r);
             for (BenchmarkRecord br : res.keys()) {
                 results.putAll(br, res.get(br));
             }
@@ -334,18 +320,22 @@
             if (warmupForkCount > 0) {
                 out.verbosePrintln("Warmup forking " + warmupForkCount + " times using command: " + Arrays.toString(commandString));
                 for (int i = 0; i < warmupForkCount; i++) {
+                    beforeBenchmark();
                     out.println("# Warmup Fork: " + (i + 1) + " of " + forkCount);
                     out.println("# VM options: " + opts);
                     doFork(server, commandString);
+                    afterBenchmark(benchmark);
                 }
             }
 
             out.verbosePrintln("Forking " + forkCount + " times using command: " + Arrays.toString(commandString));
             for (int i = 0; i < forkCount; i++) {
+                beforeBenchmark();
                 out.println("# Fork: " + (i + 1) + " of " + forkCount);
                 out.println("# VM options: " + opts);
                 Multimap<BenchmarkRecord, BenchResult> result = doFork(server, commandString);
                 results.merge(result);
+                afterBenchmark(benchmark);
             }
 
         } catch (IOException e) {
--- a/jmh-core/src/main/java/org/openjdk/jmh/runner/parameters/TimeValue.java	Sun Jan 26 13:26:06 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/runner/parameters/TimeValue.java	Mon Jan 27 18:38:20 2014 +0400
@@ -81,6 +81,10 @@
         return time;
     }
 
+    public long getTime(TimeUnit tu) {
+        return tu.convert(time, timeUnit);
+    }
+
     public TimeUnit getTimeUnit() {
         return timeUnit;
     }