Profiler.java
001 /*
002  *  Profiler.java - A simple profiling utility
003  *
004  *  Copyright (c) 2001, OntoText Lab. (http://www.ontotext.com)
005  *
006  *  This file is part of GATE (see http://gate.ac.uk/), and is free
007  *  software, licenced under the GNU Library General Public License,
008  *  Version 2, June 1991 (in the distribution as file licence.html,
009  *  and also available at http://gate.ac.uk/gate/licence.html).
010  *
011  *  Atanas Kiryakov, 05/11/01
012  *
013  *  $Id: Profiler.java 9788 2008-07-30 18:48:10Z esword $
014  */
015 
016 
017 package gate.util.profile;
018 
019 /**
020  *  This is a simple "code-internal" profiler which can be used
021  *  by introducing calls to its methods (mostly, checkPoint()) in  the code.
022  *  It allows detection and reporting of time and memory usage
023  *  between execution points as well as for the whole run. In addition allows
024  *  for collection/reporting of the resources usage by categories (types of
025  *  activity performed between the check point and the previous one).
026  *  The results are currently just dumped to the output.
027  *  <p>
028  *  Important feature is the possibility of the memory usage measurment code
029  *  to call the Garbage Collector following a specific strategy that allows
030  *  the real memory usage to be deteceted. This feature can be switched OFF
031  *  if appropriate, for example, if the *real* run-time memory usage have to
032  *  be measured rather than the precise but ideallistic one provided otherwise.
033  *  <p>
034  *  The profiler can be switched OFF using the enable(boolean) method. In such
035  *  case it ignores any calls to its checkPoint(...) method, so, basically does
036  *  nothing. This feature is usefull in cases when the profiler have to be
037  *  "stripped" from the code in order to allow non-disturbed execution.
038  *  <p>
039  *  The profiler takes care to calculate the time exlusive its footprint. It
040  *  takes account for both "net" time and the absolute duration. Also in
041  *  relation to the execution time measurment - it depends on the speed of the
042  *  machine as well as on the other applications running in parallel.
043  */
044 
045 //import java.io.PrintStream;
046 import java.util.Enumeration;
047 import java.util.Hashtable;
048 
049 import org.apache.log4j.Logger;
050 
051 public class Profiler {
052   protected static final Logger log = Logger.getLogger(Profiler.class);
053 
054   //private PrintStream m_out;
055   private boolean m_enabled = true;
056   private boolean m_garbageCollection = true;
057   /** Indicates whether just to return the string dumps (false) or also print them
058    to the std out (true)*/
059   private boolean m_doPrintToStdOut = true;
060 
061   // keeps the sum of the time spend on a category of tasks
062   // the catoegries are identified via strings which are used as
063   // keys in the table. The values in the table are Long objects, millisec.
064   private Hashtable m_categorySums;
065 
066   // keeps the time spend on the last task of a category
067   // the catoegries are identified via strings which are used as
068   // keys in the table. The values in the table are Long objects, millisec.
069   private Hashtable m_categoryLasts;
070 
071   private Runtime m_rt;
072 
073   // all the time constants below in 1/1000s
074   // the time when the profiler was intiated for the current run
075   private long m_startTime;
076   private long m_lastCheckTime, m_profilerTime, m_lastDuration;
077 
078   private long m_maxMemory, m_currMemory, m_diffMemory;
079 
080   public Profiler() {
081     m_rt = Runtime.getRuntime();
082     //m_out = System.out;
083   }
084 
085   /**
086    * Switches the profiler ON and OFF. When OFF all the methods do nothing
087    */
088   public void enable(boolean isEnabled) {
089     m_enabled = isEnabled;
090   // enable
091 
092   /**
093    * Answers is the profiler switched ON or OFF. When OFF all the methods do
094    * nothing
095    */
096   public boolean isEnabled() {
097     return m_enabled;
098   }
099 
100   /**
101    * Tell's the profiler whether to call the garbage collector when detecting
102    * memory usage or not. If switched ON the GC is called following a
103    * specific strategy as many time as needed so to collect all the memory
104    * that can be collected. This makes the profiling slower and also does not
105    * correctly account for the really memory usage of the applicaton when
106    * run without the profiler. On the other hand, with garbage collection, it
107    * is easier to detecet the amount of memory really necessary for the
108    * application.
109    */
110   public void enableGCCalling(boolean collect) {
111     m_garbageCollection = collect;
112   }
113 
114   /**
115    @see #enableGCCalling(boolean)
116    */
117   public boolean isGCCallingEnabled() {
118     return m_garbageCollection;
119   }
120 
121 
122   /**
123    * Returns the time spend by the profiler during the last run. It is the
124    * case that
125    *      net_run_time = run_duration - profiler_time
126    */
127   public long getProfilerTime() {
128     return m_profilerTime;
129   }
130 
131   /**
132    * Returns the time spend in the last run without the time
133    * spend by the profiler. It is the case that
134    *      net_run_time = run_duration - profiler_time
135    */
136   public long getNetRunTime() {
137     return m_lastCheckTime - m_profilerTime;
138   };
139 
140   /**
141    * Returns the time spend in the current run until the last check-point
142    * inclusive the time spend by the profiler. It is the case that
143    *      net_run_time = run_duration - profiler_time
144    */
145   public long getRunDuration() {
146     return m_lastCheckTime;
147 //        long auxTime1 = (System.currentTimeMillis() - m_startTime1000)/10;
148 //        return ((double)auxTime1)/100;
149   ;
150 
151   public long getLastDuration() {
152     return m_lastDuration;
153   }
154 
155 
156   /**
157    * Inialises the profiler for a new run
158    */
159   public String initRun(String runDescription) {
160     StringBuffer buf = new StringBuffer();
161     buf.append("-----------------------------------------------\n");
162     buf.append("New profiler run: " + runDescription);
163     buf.append("\n-----------------------------------------------\n");
164 
165     m_maxMemory=0;
166     m_currMemory=0;
167     m_diffMemory=0;
168     m_profilerTime=0;
169     m_startTime = System.currentTimeMillis();
170     m_lastCheckTime=0;
171     m_lastDuration=0;
172 
173     m_categorySums = new Hashtable();
174     m_categoryLasts = new Hashtable();
175     if m_doPrintToStdOut ) {
176       log.debug(buf.toString());
177     }
178     return buf.toString();
179   // initRun
180 
181   /**
182    * To be called at all execution points of interest. Detects the time
183    * and memory usage in absolute terms as well as compared to the previous
184    * execution point
185    */
186   public String checkPoint(String execPointDescr) {
187     return checkPoint(execPointDescr, new String[0], true, true, true);
188   }
189 
190   /**
191    * In addition to the variant of the method with two parameters allows:
192    * a set of categories (identified by strings) to which the preceeding
193    * fragment of code belongs; flag determining whether the description of
194    * the execution point to be displayed; flag determining whether the
195    * statistics to be shown
196    */
197   public String checkPoint(String execPointDescr, String categories[],
198       boolean showDescr, boolean showStats, boolean memoryCheck)
199   {
200     if (!m_enabled)
201       return "";
202 
203     long currTime = System.currentTimeMillis() - m_startTime;
204     m_lastDuration = currTime - m_lastCheckTime;
205 
206     if (memoryCheck) {
207       long oldMemory = m_currMemory;
208 
209       if (m_garbageCollection) {
210         do {
211           m_currMemory = m_rt.totalMemory() - m_rt.freeMemory();
212           m_rt.gc();
213           try {wait(300);catch (Exception e) {}
214           m_rt.gc();
215         while (m_currMemory > m_rt.totalMemory() - m_rt.freeMemory());
216       }
217       else {
218         m_currMemory = m_rt.totalMemory() - m_rt.freeMemory();
219       }
220 
221       m_currMemory /= 1000;
222       m_maxMemory = Math.max(m_maxMemory, m_currMemory);
223       m_diffMemory = m_currMemory - oldMemory;
224     // if (memoryCheck)
225 
226     m_lastCheckTime = System.currentTimeMillis() - m_startTime;
227     m_profilerTime += (m_lastCheckTime - currTime);
228 
229     checkCategories(categories);
230     return showResults(execPointDescr, showDescr, showStats);
231   // checkPoint
232 
233   private void checkCategories(String categs[]) {
234     int size = categs.length;
235     String categ;
236     long last, sum;
237     Long l;
238     for (int i=0; i<size; i++) {
239       categ = categs[i].toUpperCase();
240       l = (Long)m_categorySums.get(categ);
241       sum = (l==null: l.longValue();
242       sum += m_lastDuration;
243       m_categorySums.put(categ, new Long(sum));
244       m_categoryLasts.put(categ, new Long(m_lastDuration));
245     // for
246   // checkCategories
247 
248   private String showResults(String execPointDescr, boolean showDescr,
249       boolean showStats)
250   {
251     StringBuffer buff = new StringBuffer(500);
252     if (showDescr) {
253       buff.append("---------LOG: ");
254       buff.append(execPointDescr);
255       buff.append("---------");
256     }
257 
258     if (showStats) {
259       buff.append("\nMemory: ");
260       buff.append(m_currMemory);
261       buff.append("k; change: ");
262       buff.append(m_diffMemory);
263       buff.append("k; max: ");
264       buff.append(m_maxMemory);
265       buff.append("k; Net time:   ");
266       buff.append(printTime(getNetRunTime()));
267       buff.append("; since prev. millisecs: ");
268       buff.append(m_lastDuration);
269 //            buff.append("; profiler time: ");
270 //            buff.append(printTime(m_profilerTime));
271 //            buff.append("; duration: ");
272 //            buff.append(printTime(m_lastCheckTime));
273     }
274 
275     if (buff.length() && m_doPrintToStdOut) {
276         log.debug(buff.toString());
277     }
278     return buff.toString();
279   // showResults
280 
281   /**
282    * Returns 0 if the category was not found
283    */
284   public long getCategoryTimeSum(String category) {
285     Long sum = (Long)m_categorySums.get(category.toUpperCase());
286     return (sum == null: sum.longValue();
287   // getCategoryTimeSum
288 
289   /**
290    * Returns 0 if the category was not found
291    */
292   public long getCategoryTimeLast(String category) {
293     Long sum = (Long)m_categoryLasts.get(category.toUpperCase());
294     return (sum == null: sum.longValue();
295   // getCategoryTimeSum
296 
297   /**
298    * Prints the time for all the categories of activities
299    */
300   public void showCategoryTimes() {
301     log.debug("Time spent by categories:");
302     Enumeration categNames = m_categorySums.keys();
303     String categ;
304     while (categNames.hasMoreElements()) {
305       categ = (String)categNames.nextElement();
306       showCategoryTime(categ);
307     // while
308   // showCategoryTimes
309 
310   /**
311    * Prints the time for certain category of activities
312    */
313   public void showCategoryTime(String categ) {
314     log.debug(categ + ", sum=" +
315             printTime(getCategoryTimeSum(categ)) +
316             ", last=" + printTime(getCategoryTimeLast(categ)));
317   // showCategoryTimes
318 
319   /**
320    * An auxiliary routine printing time in "nnn.nns" format
321    */
322   public String printTime(long timeMillis) {
323     long round = timeMillis/1000;
324     long remaind = (timeMillis % 1000)/10;
325     StringBuffer buff = new StringBuffer(10);
326     buff.append(round);
327     buff.append(".");
328     buff.append(remaind);
329     buff.append("s");
330     return buff.toString();
331   // printTime
332 
333   /**
334    * An auxiliary routine printing in a string speed
335    */
336   public String printSpeed(long timeMillis,
337       double whatever, String whateverMeasure)
338   {
339     double speed1000 = (doublewhatever/ timeMillis;
340     long round = (long)((double)speed1000*1000);
341     long remaind =  (long)(((double)speed1000*100000100 * round);
342     StringBuffer buff = new StringBuffer(10);
343     buff.append(round);
344     buff.append(".");
345     buff.append(remaind);
346     buff.append(whateverMeasure);
347     buff.append("/s");
348     return buff.toString();
349   // printTime
350 
351   /**
352    * An auxiliary routine printing time, avg. time, and avg. speed for
353    * a category
354    */
355   public void printCategAvg(String categ, long items,
356       double volume, String whateverMeasure)
357   {
358     long time = getCategoryTimeSum(categ);
359     if (time==0) {
360       log.debug("Category \"" + categ + "\" not found");
361     }
362 
363     log.debug("Category \"" + categ + "\",  Time= " +
364         printTime(time"; avg. time= " +
365         printTime(time/items+  "; speed= " +
366         printSpeed(time, volume, whateverMeasure));
367   // printCategAvg
368 
369   /**
370    * Sets the profiler to print (or not) to the standard output.
371    * The default behaviour is - print to std out.
372    @param doPrint whether or not to print to std out.
373    */
374   public void printToSystemOut(boolean doPrint){
375     m_doPrintToStdOut = doPrint;
376   // printToSystemOut(doPrint);
377 // Profiler