source: trunk/java-utils-alignment/src/main/java/de/ugoe/cs/util/StopWatch.java @ 1707

Last change on this file since 1707 was 1707, checked in by rkrimmel, 10 years ago

Possibility to save intermediate results

File size: 9.9 KB
RevLine 
[1119]1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
[1128]15package de.ugoe.cs.util;
[1119]16
17import java.io.PrintStream;
[1707]18import java.io.Serializable;
[1119]19import java.text.DecimalFormat;
20import java.util.HashMap;
21import java.util.LinkedList;
22import java.util.List;
23import java.util.Map;
24
25/**
26 * <p>
[1128]27 * This is a simple implementation for a stop watch that can be used for performance measures.
28 * A stop watch can be used to measure several splits. Each split is started and stopped using the
29 * same id provided to the {@link #start(String)} and {@link #stop(String)} methods. The measured
30 * durations can be retrieved afterwards using {@link #getDuration(String)}.
31 * {@link #dumpStatistics(PrintStream)} is a convenience method useful to effectively dump all
32 * information for the different splits.
[1119]33 * </p>
34 *
35 * @author Patrick Harms
36 */
[1707]37public class StopWatch implements Serializable{
[1119]38   
39    /**
[1707]40         *
41         */
42        private static final long serialVersionUID = -4216393284789336830L;
43        /**
[1128]44     * the splits hold internally
[1119]45     */
[1128]46    private Map<String, Split> mSplits = new HashMap<String, Split>();
[1119]47
48    /**
[1128]49     * starts a split with the given id. If the split with the id is already started, an
50     * {@link IllegalStateException} is thrown.
[1119]51     *
[1128]52     * @param id the id of the split to be started
53     *
54     * @throws IllegalStateException if the split is already started
[1119]55     */
[1128]56    public void start(String id) throws IllegalStateException {
57        Split split = mSplits.get(id);
[1119]58       
[1128]59        if (split == null) {
60            split = new Split(id);
61            mSplits.put(id, split);
[1119]62        }
63       
[1128]64        split.start();
[1119]65    }
66   
67    /**
[1128]68     * stops a split with the given id. If the split with the id is already stopped, an
69     * {@link IllegalStateException} is thrown. If no split with the given id exists, an
70     * {@link IllegalArgumentException} is thrown.
[1119]71     *
[1128]72     * @param id the id of the split to be stopped
73     *
74     * @throws IllegalStateException    if the split is not running
75     * @throws IllegalArgumentException if the split with the given id does not exist
[1119]76     */
[1128]77    public void stop(String id) throws IllegalStateException, IllegalArgumentException {
78        Split split = mSplits.get(id);
[1119]79       
[1128]80        if (split == null) {
81            throw new IllegalArgumentException("split with id " + id + " does not exist");
[1119]82        }
83       
[1128]84        split.stop();
[1119]85    }
86   
87    /**
[1128]88     * returns the duration of a split with the given id. If the split with the id is currently
89     * running, it is stopped. If no split with the given id exists, an
90     * {@link IllegalArgumentException} is thrown.
[1119]91     *
[1128]92     * @param id the id of the split for which the duration shall be returned
93     *
94     * @return the duration measured for the split
95     *
96     * @throws IllegalArgumentException if the split with the given id does not exist
[1119]97     */
[1128]98    public long getDuration(String id) throws IllegalArgumentException {
99        Split split = mSplits.get(id);
100       
101        if (split == null) {
102            throw new IllegalArgumentException("split with id " + id + " does not exist");
[1119]103        }
104       
[1128]105        if (split.isRunning()) {
106            split.stop();
[1119]107        }
108       
[1128]109        return split.getDuration();
[1119]110    }
111
112    /**
[1128]113     * resets the stop watch and clears all splits
[1119]114     */
[1128]115    public void reset() {
116        mSplits.clear();
117    }
118   
119    /**
120     * dumps the statistics about the splits. Splits still running are stopped. The method checks
121     * if the longest split also covers the other splits. If so, it considers this split as a
122     * kind of overall duration and dumps the proportion of all other splits to this split in
123     * percentage.
124     *
125     * @param out the stream to dump the statistics to
126     */
[1119]127    public void dumpStatistics(PrintStream out) {
[1128]128        if (mSplits.size() <= 0) {
129            throw new IllegalStateException("no splits registered that could be dumped");
[1119]130        }
131       
132        Map<String, Long> durations = new HashMap<String, Long>();
133       
134        // get durations
[1128]135        for (String id : mSplits.keySet()) {
[1119]136            durations.put(id, getDuration(id));
137        }
138       
139        // sort by duration
140        List<String> sortedIds = new LinkedList<String>();
141        int maxIdLength = 0;
142       
143        for (Map.Entry<String, Long> entry : durations.entrySet()) {
144            boolean added = false;
145            for (int i = 0; i < sortedIds.size(); i++) {
146                if (durations.get(sortedIds.get(i)) >= entry.getValue()) {
147                    sortedIds.add(i, entry.getKey());
148                    added = true;
149                    break;
150                }
151            }
152           
153            if (!added) {
154                sortedIds.add(entry.getKey());
155            }
156           
157            maxIdLength = Math.max(maxIdLength, entry.getKey().length());
158        }
159       
160        // get longest duration and check whether it spans all other entries
161        String id = sortedIds.get(sortedIds.size() - 1);
[1128]162        Split longestWatch = mSplits.get(id);
[1119]163        boolean longestDurationCoversOthers = true;
164       
[1128]165        for (Map.Entry<String, Split> watch : mSplits.entrySet()) {
166            if ((watch.getValue().getFirstStart() < longestWatch.getFirstStart()) ||
167                (watch.getValue().getLastStop() > longestWatch.getLastStop()))
[1119]168            {
169                longestDurationCoversOthers = false;
170                break;
171            }
172        }
173       
174        // no finally start the dumping
175        out.println();
176        out.println("Watch Statistics");
177        out.println("================");
178
179        for (String sortedId : sortedIds) {
180            out.print(sortedId);
181           
182            for (int i = sortedId.length(); i <= maxIdLength; i++) {
183                out.print(' ');
184            }
185           
[1128]186            out.print(": ");
[1119]187           
188            out.print(durations.get(sortedId));
189            out.print(" ms");
190           
[1128]191            out.print(" (");
192            out.print(mSplits.get(sortedId).getNoOfStarts());
193            out.print(" starts");
194           
195            //out.print(", ");
196            //out.print(1000 * durations.get(sortedId) / mSplits.get(sortedId).getNoOfStarts());
197            //out.print(" ms per 1000 starts");
198
[1119]199            if (longestDurationCoversOthers) {
[1128]200                out.print(", ");
[1119]201                out.print(DecimalFormat.getPercentInstance().format
[1128]202                              ((double) durations.get(sortedId) / longestWatch.getDuration()));
203                out.print(" of overall duration");
[1119]204            }
[1128]205           
206            out.println(')');
[1119]207        }
208       
209        out.println();
210    }
[1128]211   
212    /**
213     * internally used to store splits
214     */
215    private static class Split {
216       
217        /**
218         * the id of the split
219         */
220        private String id;
221       
222        /**
223         * the system time of the first start of the split
224         */
225        private long firstStart = -1;
226       
227        /**
228         * the system time of the last start of the split
229         */
230        private long lastStart = -1;
231       
232        /**
233         * the system time of the last stop of the split
234         */
235        private long lastStop = -1;
236       
237        /**
238         * the duration so far for the split (excluding the time since the last start)
239         */
240        private long duration = 0;
241       
242        /**
243         * the number of starts or the splits
244         */
245        private long noOfStarts = 0;
246       
247        /**
248         * initializes the split with its id
249         */
250        private Split(String id) {
251            this.id = id;
252        }
253       
254        /**
255         * starts the split if it is not already started
256         */
257        private void start() throws IllegalStateException {
258            if (lastStart > -1) {
259                throw new IllegalStateException("split with id " + id + " already running");
260            }
261           
262            lastStart = System.currentTimeMillis();
263           
264            if (firstStart < 0) {
265                firstStart = lastStart;
266            }
267           
268            noOfStarts++;
269        }
270       
271        /**
272         * checks if the split is currently running
273         */
274        private boolean isRunning() {
275            return (lastStart > -1);
276        }
277       
278        /**
279         * stops the split if it is not already stopped
280         */
281        private void stop() throws IllegalStateException {
282            if (lastStart < 0) {
283                throw new IllegalStateException("split with id " + id + " not running");
284            }
285           
286            lastStop = System.currentTimeMillis();
287            duration += lastStop - lastStart;
288            lastStart = -1;
289        }
290       
291        /**
292         * returns the fist start of the split
293         */
294        private long getFirstStart() {
295            return firstStart;
296        }
297
298        /**
299         * returns the last stop of the split
300         */
301        private long getLastStop() {
302            return lastStop;
303        }
304
305        /**
306         * returns the duration of the split measured so far excluding the time since the last
307         * start if the split is currently started
308         */
309        private long getDuration() {
310            return duration;
311        }
312
313        /**
314         * returns the number of starts for the split
315         */
316        private long getNoOfStarts() {
317            return noOfStarts;
318        }
319    }
[1119]320}
Note: See TracBrowser for help on using the repository browser.