View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.common.statistic;
25  
26  import net.jcip.annotations.NotThreadSafe;
27  import org.modeshape.common.math.Duration;
28  import org.modeshape.common.math.DurationOperations;
29  
30  /**
31   * Provides a mechanism to measure time in the same way as a physical stopwatch.
32   */
33  @NotThreadSafe
34  public class Stopwatch implements Comparable<Stopwatch> {
35  
36      private long lastStarted;
37      private final SimpleStatistics<Duration> stats;
38      private final DetailedStatistics<Duration> detailedStats;
39      private String description;
40  
41      public Stopwatch() {
42          this(true);
43      }
44  
45      public Stopwatch( boolean detailedStats ) {
46          this(detailedStats, null);
47      }
48  
49      public Stopwatch( boolean detailedStats,
50                        String description ) {
51          this.description = description != null ? description : "";
52          this.detailedStats = detailedStats ? new DetailedStatistics<Duration>(new DurationOperations()) : null;
53          this.stats = detailedStats ? this.detailedStats : new SimpleStatistics<Duration>(new DurationOperations());
54          reset();
55      }
56  
57      public String getDescription() {
58          return this.description;
59      }
60  
61      /**
62       * Start the stopwatch and begin recording the statistics a new run. This method does nothing if the stopwatch is already
63       * {@link #isRunning() running}
64       * 
65       * @see #isRunning()
66       */
67      public void start() {
68          if (!this.isRunning()) {
69              this.lastStarted = System.nanoTime();
70          }
71      }
72  
73      /**
74       * Stop the stopwatch and record the statistics for the latest run. This method does nothing if the stopwatch is not currently
75       * {@link #isRunning() running}
76       * 
77       * @see #isRunning()
78       */
79      public void stop() {
80          if (this.isRunning()) {
81              long duration = System.nanoTime() - this.lastStarted;
82              this.lastStarted = 0l;
83              this.stats.add(new Duration(duration));
84          }
85      }
86  
87      /**
88       * Return the number of runs (complete starts and stops) this stopwatch has undergone.
89       * 
90       * @return the number of runs.
91       * @see #isRunning()
92       */
93      public int getCount() {
94          return this.stats.getCount();
95      }
96  
97      /**
98       * Return whether this stopwatch is currently running.
99       * 
100      * @return true if running, or false if not
101      */
102     public boolean isRunning() {
103         return this.lastStarted != 0;
104     }
105 
106     /**
107      * Get the total duration that this stopwatch has recorded.
108      * 
109      * @return the total duration, or an empty duration if this stopwatch has not been used since creation or being
110      *         {@link #reset() reset}
111      */
112     public Duration getTotalDuration() {
113         return this.stats.getTotal();
114     }
115 
116     /**
117      * Get the average duration that this stopwatch has recorded.
118      * 
119      * @return the average duration, or an empty duration if this stopwatch has not been used since creation or being
120      *         {@link #reset() reset}
121      */
122     public Duration getAverageDuration() {
123         return this.stats.getMean();
124     }
125 
126     /**
127      * Get the median duration that this stopwatch has recorded.
128      * 
129      * @return the median duration, or an empty duration if this stopwatch has not been used since creation or being
130      *         {@link #reset() reset}
131      */
132     public Duration getMedianDuration() {
133         return this.detailedStats != null ? this.detailedStats.getMedian() : new Duration(0l);
134     }
135 
136     /**
137      * Get the minimum duration that this stopwatch has recorded.
138      * 
139      * @return the total minimum, or an empty duration if this stopwatch has not been used since creation or being
140      *         {@link #reset() reset}
141      */
142     public Duration getMinimumDuration() {
143         return this.stats.getMinimum();
144     }
145 
146     /**
147      * Get the maximum duration that this stopwatch has recorded.
148      * 
149      * @return the maximum duration, or an empty duration if this stopwatch has not been used since creation or being
150      *         {@link #reset() reset}
151      */
152     public Duration getMaximumDuration() {
153         return this.stats.getMaximum();
154     }
155 
156     /**
157      * Return this stopwatch's simple statistics.
158      * 
159      * @return the statistics
160      * @see #getDetailedStatistics()
161      */
162     public SimpleStatistics<Duration> getSimpleStatistics() {
163         return this.stats;
164     }
165 
166     /**
167      * Return this stopwatch's detailed statistics, if they are being kept.
168      * 
169      * @return the statistics
170      * @see #getSimpleStatistics()
171      */
172     public DetailedStatistics<Duration> getDetailedStatistics() {
173         return this.detailedStats;
174     }
175 
176     /**
177      * Return true if detailed statistics are being kept.
178      * 
179      * @return true if {@link #getDetailedStatistics() detailed statistics} are being kept, or false if only
180      *         {@link #getSimpleStatistics() simple statistics} are being kept.
181      */
182     public boolean isDetailedStatistics() {
183         return this.detailedStats != null;
184     }
185 
186     /**
187      * Return the histogram of this stopwatch's individual runs. Two different kinds of histograms can be created. The first kind
188      * is a histogram where all of the buckets are distributed normally and all have the same width. In this case, the 'numSigmas'
189      * should be set to 0.
190      * <p>
191      * <i>Note: if only {@link #getSimpleStatistics() simple statistics} are being kept, the resulting histogram is always empty.
192      * <p>
193      * The second kind of histogram is more useful when most of the data that is clustered near one value. This histogram is
194      * focused around the values that are up to 'numSigmas' above and below the {@link #getMedianDuration() median}, and all
195      * values outside of this range are placed in the first and last bucket.
196      * </p>
197      * 
198      * @param numSigmas the number of standard deviations from the {@link #getMedianDuration() median}, or 0 if the buckets of the
199      *        histogram should be evenly distributed
200      * @return the histogram
201      */
202     public Histogram<Duration> getHistogram( int numSigmas ) {
203         return this.detailedStats != null ? this.detailedStats.getHistogram(numSigmas) : new Histogram<Duration>(
204                                                                                                                  this.stats.getMathOperations());
205     }
206 
207     /**
208      * Reset this stopwatch and clear all statistics.
209      */
210     public void reset() {
211         this.lastStarted = 0l;
212         this.stats.reset();
213     }
214 
215     public int compareTo( Stopwatch that ) {
216         return this.getTotalDuration().compareTo(that.getTotalDuration());
217     }
218 
219     @Override
220     public String toString() {
221         StringBuilder sb = new StringBuilder();
222         sb.append(this.getTotalDuration());
223         if (this.stats.getCount() > 1) {
224             sb.append(" (");
225             sb.append(this.stats.getCount()).append(" samples, avg=");
226             sb.append(this.getAverageDuration());
227             sb.append("; median=");
228             sb.append(this.getMedianDuration());
229             sb.append("; min=");
230             sb.append(this.getMinimumDuration());
231             sb.append("; max=");
232             sb.append(this.getMaximumDuration());
233             sb.append(")");
234         }
235         return sb.toString();
236     }
237 
238 }