001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.experiment.agent;
031
032import java.util.HashMap;
033import java.util.Map;
034import java.util.Map.Entry;
035
036import org.apache.commons.math.stat.descriptive.SummaryStatistics;
037import org.openimaj.time.NanoTimer;
038import org.openimaj.time.Timer;
039
040import com.bethecoder.ascii_table.ASCIITable;
041
042/**
043 * A class for tracking various execution times and generating
044 * statistics.
045 * 
046 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
047 */
048public class TimeTracker {
049        private static Map<String, SummaryStatistics> times = new HashMap<String, SummaryStatistics>();
050        
051        /**
052         * Accumulate the given duration into the statistics with the given identifier
053         * 
054         * @param identifier the identifier
055         * @param timer the timer to retrieve the dureation from
056         */
057        public static synchronized void accumulate(String identifier, NanoTimer timer) {
058                accumulate(identifier, timer.duration());
059        }
060        
061        /**
062         * Accumulate the given duration into the statistics with the given identifier
063         * 
064         * @param identifier the identifier
065         * @param timer the timer to retrieve the dureation from
066         */
067        public static synchronized void accumulate(String identifier, Timer timer) {
068                accumulate(identifier, (long)(timer.duration() * 1e6));
069        }
070        
071        /**
072         * Accumulate the given duration into the statistics with the given identifier
073         * 
074         * @param identifier the identifier
075         * @param nanoTime the duration to accumulate in nano seconds
076         */
077        public static synchronized void accumulate(String identifier, long nanoTime) {
078                SummaryStatistics t = times.get(identifier);
079                
080                if (t == null) times.put(identifier, t = new SummaryStatistics());
081                
082                t.addValue(nanoTime);
083        }
084        
085        /**
086         * Reset all the previously accumulated times, returning them.
087         * @return the old times
088         */
089        public static synchronized Map<String, SummaryStatistics> reset() {
090                Map<String, SummaryStatistics> oldTimes = times;
091                times = new HashMap<String, SummaryStatistics>();
092                return oldTimes;
093        }
094        
095        /**
096         * Get a copy of all the accumulated data
097         * @return a copy of all the accumulated data
098         */
099        public static synchronized Map<String, SummaryStatistics> getTimes() {
100                HashMap<String, SummaryStatistics> ret = new HashMap<String, SummaryStatistics>();
101                
102                for (Entry<String, SummaryStatistics> e : times.entrySet()) {
103                        ret.put(e.getKey(), e.getValue().copy());
104                }
105                
106                return ret;
107        }
108
109        /**
110         * Add any times from the given map that are not present in the
111         * internal map to the internal map.
112         * 
113         * @param timesToAdd the times to add
114         */
115        public static void addMissing(Map<String, SummaryStatistics> timesToAdd) {
116                for (Entry<String, SummaryStatistics> e : timesToAdd.entrySet()) {
117                        if (!times.containsKey(e.getValue()))
118                                times.put(e.getKey(), e.getValue());
119                }
120        }
121        
122        /**
123         * Pretty-print a map of times
124         * 
125         * @param times the times
126         * @return a string representation of the times
127         */
128        public static String format(Map<String, SummaryStatistics> times) {
129                String [] header = {"Timer Identifier", "Recorded Time"};
130                String [][] data = new String[times.size()][];
131                
132                int i = 0;
133                for (Entry<String, SummaryStatistics> e : times.entrySet()) {
134                        data[i++] = new String [] { e.getKey(), format(e.getValue()) };
135                }
136                
137                return ASCIITable.getInstance().getTable(header, data);
138        }
139        
140        /**
141         * Pretty print a time
142         * @param ss the stats defining the time
143         * @return a string representing the time
144         */
145        public static String format(SummaryStatistics ss) {
146                if (ss.getN() == 1) {
147                        return formatTime(ss.getMean());
148                } 
149                return formatTime(ss.getMean(), ss.getStandardDeviation());
150        }
151
152        private static String formatTime(double time) {
153                long ns = (long) time;
154                
155                if (ns < 1e3) {
156                        return time + "ns";
157                }
158                if (ns < 1e6) {
159                        return (time / 1e3) + "us";
160                }
161                if (ns < 1e9) {
162                        return (time / 1e6) + "ms";
163                }
164                
165                double secs = (time / 1e9);
166                if (secs < 60) {
167                        return String.format("%2.5ss", secs);
168                }
169                
170                long mins = (long)(secs / 60);
171                double rsecs = secs - (mins * 60);
172                if (mins < 60) {
173                        return mins + "m" + String.format("%2.5ss", rsecs);
174                }
175        
176                long hrs = (long)(mins / 60);
177                long rmins = mins - (hrs * 60);
178                if (hrs < 24) { 
179                        return hrs + "h" + rmins + "m" + String.format("%2.5ss", rsecs);
180                }
181                
182                long days = (long)(hrs / 24);
183                long rhrs = days - (hrs * 24);
184                if (hrs < 24) { 
185                        return days + "d" + rhrs + "h" + rmins + "m" + String.format("%2.5ss", rsecs);
186                }
187        
188                return time+"ns";
189        }
190
191        private static String formatTime(double mean, double standardDeviation) {
192                return formatTime(mean) + " (SD = " +formatTime(standardDeviation) + ")";
193        }
194}