001    /*
002    Galois, a framework to exploit amorphous data-parallelism in irregular
003    programs.
004    
005    Copyright (C) 2010, The University of Texas at Austin. All rights reserved.
006    UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS SOFTWARE
007    AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR ANY
008    PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF PERFORMANCE, AND ANY
009    WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF DEALING OR USAGE OF TRADE.
010    NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH RESPECT TO THE USE OF THE
011    SOFTWARE OR DOCUMENTATION. Under no circumstances shall University be liable
012    for incidental, special, indirect, direct or consequential damages or loss of
013    profits, interruption of business, or related expenses which may arise from use
014    of Software or Documentation, including but not limited to those resulting from
015    defects in Software and/or Documentation, or loss or inaccuracy of data of any
016    kind.
017    
018    File: ThreadTimer.java 
019    
020    */
021    
022    
023    
024    package util;
025    
026    import java.lang.management.GarbageCollectorMXBean;
027    import java.lang.management.ManagementFactory;
028    import java.util.List;
029    
030    /**
031     * Class that allows per-thread timing that excludes GC time. This class is
032     * thread safe.
033     * 
034     * The class is used by instantiating a new object at each "tick" The time
035     * between two ticks (exclusive of GC time) can then be calculated using the
036     * static method elapsedTime().
037     * 
038     * Note that this class requires the use of a stop-the-world collector (as it
039     * assumes that no mutator activity occurs while the GC is running)
040     * 
041     */
042    public final class ThreadTimer {
043      private final static List<GarbageCollectorMXBean> garbageCollectorMXBeans;
044    
045      static {
046        garbageCollectorMXBeans = ManagementFactory.getGarbageCollectorMXBeans();
047      }
048    
049      /**
050       * Creates a new tick, fixing the start time of the current event
051       * 
052       * @return a tick object
053       */
054      public static Tick tick() {
055        return new Tick();
056      }
057    
058      /**
059       * A moment in time
060       */
061      public static class Tick {
062        private long sysTime;
063        private long gcTime;
064    
065        private Tick() {
066          /*
067           * Here is the problem: we want to make sure that we mark the system
068           * time and the gc time consistently. However, between recording the
069           * system time and recording the GC time, a garbage collection might
070           * occur, which would make the numbers inconsistent with each other.
071           * 
072           * What we will do is record the number of GCs executed, then record
073           * the system time and GC time, then re-check the number of GCs
074           * executed. If the two numbers agree, then we have recorded
075           * consistent times. Otherwise, we must re-do the measurement.
076           */
077          long gcCountBegin, gcCountEnd;
078          do {
079            gcCountBegin = numGCs();
080            sysTime = milliTime(); // don't want a GC between this line
081            gcTime = milliGcTime(); // and this one
082            gcCountEnd = numGCs();
083          } while (gcCountBegin != gcCountEnd);
084        }
085    
086        /**
087         * Returns the elapsed time between the two Ticks. The returned value does
088         * not include any time spent by the JVM in garbage collection.
089         * 
090         * @param withoutGc  true if garbage collection time should not be included in result
091         * @param end        the tick marking the end time
092         * @return  elapsed time in milliseconds.
093         */
094        public long elapsedTime(boolean withoutGc, Tick end) {
095          return (end.sysTime - sysTime) - (withoutGc ? (end.gcTime - gcTime) : 0);
096        }
097    
098        private long milliTime() {
099          return System.nanoTime() / 1000000;
100        }
101    
102        private long milliGcTime() {
103          long result = 0;
104          for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMXBeans) {
105            // the collection time is already in milliseconds,
106            result += Math.max(0, garbageCollectorMXBean.getCollectionTime());
107          }
108          return result;
109        }
110    
111        private long numGCs() {
112          long result = 0;
113          for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMXBeans) {
114            result += garbageCollectorMXBean.getCollectionCount();
115          }
116          return result;
117        }
118      }
119    }