You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2019/10/11 13:28:20 UTC

[commons-rng] 08/16: Progress tracker to estimate time remaining.

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rng.git

commit 55c7c83b9d9214c4e6bc806edaf94a3deff7f0a8
Author: aherbert <ah...@apache.org>
AuthorDate: Mon Oct 7 11:51:04 2019 +0100

    Progress tracker to estimate time remaining.
---
 .../rng/examples/stress/StressTestCommand.java     | 111 +++++++++++++++++++--
 1 file changed, 102 insertions(+), 9 deletions(-)

diff --git a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java
index 286f825..84b4ac3 100644
--- a/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java
+++ b/commons-rng-examples/examples-stress/src/main/java/org/apache/commons/rng/examples/stress/StressTestCommand.java
@@ -18,6 +18,7 @@ package org.apache.commons.rng.examples.stress;
 
 import org.apache.commons.rng.UniformRandomProvider;
 import org.apache.commons.rng.core.source64.RandomLongSource;
+import org.apache.commons.rng.examples.stress.LogUtils.LogLevel;
 import org.apache.commons.rng.simple.RandomSource;
 
 import picocli.CommandLine.Command;
@@ -35,6 +36,7 @@ import java.nio.file.StandardOpenOption;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.Formatter;
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.Callable;
@@ -338,7 +340,7 @@ class StressTestCommand implements Callable<Void> {
 
         LogUtils.info("Running stress test ...");
         LogUtils.info("Shutdown by creating stop file: %s",  stopFile);
-        final ProgressTracker progressTracker = new ProgressTracker(countTrials(stressTestData));
+        final ProgressTracker progressTracker = new ProgressTracker(countTrials(stressTestData), taskCount);
 
         // Run tasks with parallel execution.
         final ExecutorService service = Executors.newFixedThreadPool(taskCount);
@@ -461,7 +463,7 @@ class StressTestCommand implements Callable<Void> {
                 // Log the decision
                 LogUtils.info("%s existing output file: %s", outputMode, output);
                 if (outputMode == StressTestCommand.OutputMode.SKIP) {
-                    progressTracker.incrementProgress();
+                    progressTracker.incrementProgress(0);
                     continue;
                 }
             }
@@ -517,23 +519,38 @@ class StressTestCommand implements Callable<Void> {
         private int count;
         /** The timestamp of the last progress report. */
         private long timestamp;
+        /** The level of parallelisation. */
+        private final int parallelTasks;
+        /** The total time of all completed tasks (in milliseconds). */
+        private long totalTime;
+        /** The number of tasks completed with a time (i.e. were not skipped). */
+        private int completed;
 
         /**
          * Create a new instance.
          *
          * @param total The total progress.
+         * @param parallelTasks The number of parallel tasks.
          */
-        ProgressTracker(int total) {
+        ProgressTracker(int total, int parallelTasks) {
             this.total = total;
+            this.parallelTasks = parallelTasks;
             showProgress();
         }
 
         /**
-         * Increment the progress.
+         * Signal that a task has completed in a specified time.
+         *
+         * @param taskTime The time for the task (milliseconds).
          */
-        void incrementProgress() {
+        void incrementProgress(long taskTime) {
             synchronized (this) {
                 count++;
+                // Used to compute the average task time
+                if (taskTime != 0) {
+                    totalTime += taskTime;
+                    completed++;
+                }
                 showProgress();
             }
         }
@@ -551,8 +568,85 @@ class StressTestCommand implements Callable<Void> {
             final long current = System.currentTimeMillis();
             if (current - timestamp > REPORT_INTERVAL) {
                 timestamp = current;
-                LogUtils.info("Progress %d / %d (%.2f%%)", count, total, 100.0 * count / total);
+                final StringBuilder sb = new StringBuilder(80);
+                try (Formatter formatter = new Formatter(sb)) {
+                    formatter.format("Progress %d / %d (%.2f%%)", count, total, 100.0 * count / total);
+                    LogUtils.info(appendRemaining(sb).toString());
+                }
+            }
+        }
+
+        /**
+         * Compute an estimate of the time remaining and append to the progress.
+         *
+         * @param sb String Builder.
+         * @return the string builder
+         */
+        private StringBuilder appendRemaining(StringBuilder sb) {
+            if (completed == 0) {
+                // No estimate possible.
+                return sb;
+            }
+
+            final int remaining = total - count;
+            if (remaining < parallelTasks) {
+                // No more tasks to submit so the last estimate was as good as we can make it.
+                return sb;
+            }
+
+            // Estimate time remaining using the average runtime per task
+            // multiplied by the number of parallel remaining tasks (rounded down).
+            // Parallel remaining is the number of batches required to execute the
+            // remaining tasks in parallel.
+            final long parallelRemaining = remaining / parallelTasks;
+            final long millis = (totalTime * parallelRemaining) / completed;
+
+            // HH:mm:ss format
+            if (LogUtils.isLoggable(LogLevel.DEBUG)) {
+                sb.append(". Average task time = ");
+                hms(sb, totalTime / completed);
+            }
+            sb.append(". Remaining = ");
+            return hms(sb, millis);
+        }
+
+        /**
+         * Append the milliseconds using {@code HH::mm:ss} format.
+         *
+         * @param sb String Builder.
+         * @param millis Milliseconds.
+         * @return the string builder
+         */
+        static StringBuilder hms(StringBuilder sb, final long millis) {
+            final long hours = TimeUnit.MILLISECONDS.toHours(millis);
+            long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
+            long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
+            // Truncate to interval [0,59]
+            seconds -= TimeUnit.MINUTES.toSeconds(minutes);
+            minutes -= TimeUnit.HOURS.toMinutes(hours);
+
+            append00(sb, hours).append(':');
+            append00(sb, minutes).append(':');
+            return append00(sb, seconds);
+        }
+
+        /**
+         * Append the ticks to the string builder in the format {@code %02d}.
+         *
+         * @param sb String Builder.
+         * @param ticks Ticks.
+         * @return the string builder
+         */
+        static StringBuilder append00(StringBuilder sb, long ticks) {
+            if (ticks == 0) {
+                sb.append("00");
+            } else {
+                if (ticks < 10) {
+                    sb.append('0');
+                }
+                sb.append(ticks);
             }
+            return sb;
         }
     }
 
@@ -624,15 +718,14 @@ class StressTestCommand implements Callable<Void> {
                 return;
             }
 
+            long nanoTime = 0;
             try {
                 printHeader();
 
                 Object exitValue;
-                long nanoTime;
                 if (cmd.dryRun) {
                     // Do not do anything
                     exitValue = "N/A";
-                    nanoTime = 0;
                 } else {
                     // Run the sub-process
                     final long startTime = System.nanoTime();
@@ -645,7 +738,7 @@ class StressTestCommand implements Callable<Void> {
             } catch (final IOException ex) {
                 throw new ApplicationException("Failed to run task: " + ex.getMessage(), ex);
             } finally {
-                progressTracker.incrementProgress();
+                progressTracker.incrementProgress(TimeUnit.NANOSECONDS.toMillis(nanoTime));
             }
         }