1   /*
2    *  Profiler.java - A simple profiling utility
3    *
4    *  Copyright (c) 2001, OntoText Lab. (http://www.ontotext.com)
5    *
6    *  This file is part of GATE (see http://gate.ac.uk/), and is free
7    *  software, licenced under the GNU Library General Public License,
8    *  Version 2, June 1991 (in the distribution as file licence.html,
9    *  and also available at http://gate.ac.uk/gate/licence.html).
10   *
11   *  Atanas Kiryakov, 05/11/01
12   *
13   *  $Id: Profiler.java,v 1.1 2001/11/05 16:11:24 nasso Exp $
14   */
15  
16  
17  package gate.util.profile;
18  
19  /**
20   *  This is a simple "code-internal" profiler which can be used
21   *  by introducing calls to its methods (mostly, checkPoint()) in  the code.
22   *  It allows detection and reporting of time and memory usage
23   *  between execution points as well as for the whole run. In addition allows
24   *  for collection/reporting of the resources usage by categories (types of
25   *  activity performed between the check point and the previous one).
26   *  The results are currently just dumped to the output.
27   *  <p>
28   *  Important feature is the possibility of the memory usage measurment code
29   *  to call the Garbage Collector following a specific strategy that allows
30   *  the real memory usage to be deteceted. This feature can be switched OFF
31   *  if appropriate, for example, if the *real* run-time memory usage have to
32   *  be measured rather than the precise but ideallistic one provided otherwise.
33   *  <p>
34   *  The profiler can be switched OFF using the enable(boolean) method. In such
35   *  case it ignores any calls to its checkPoint(...) method, so, basically does
36   *  nothing. This feature is usefull in cases when the profiler have to be
37   *  "stripped" from the code in order to allow non-disturbed execution.
38   *  <p>
39   *  The profiler takes care to calculate the time exlusive its footprint. It
40   *  takes account for both "net" time and the absolute duration. Also in
41   *  relation to the execution time measurment - it depends on the speed of the
42   *  machine as well as on the other applications running in parallel.
43   */
44  
45  import java.io.*;
46  import java.util.*;
47  
48  public class Profiler {
49  
50    private PrintStream m_out;
51    private boolean m_enabled = true;
52    private boolean m_garbageCollection = true;
53  
54    // keeps the sum of the time spend on a category of tasks
55    // the catoegries are identified via strings which are used as
56    // keys in the table. The values in the table are Long objects, millisec.
57    private Hashtable m_categorySums;
58  
59    // keeps the time spend on the last task of a category
60    // the catoegries are identified via strings which are used as
61    // keys in the table. The values in the table are Long objects, millisec.
62    private Hashtable m_categoryLasts;
63  
64    private Runtime m_rt;
65  
66    // all the time constants below in 1/1000s
67    // the time when the profiler was intiated for the current run
68    private long m_startTime;
69    private long m_lastCheckTime, m_profilerTime, m_lastDuration;
70  
71    private long m_maxMemory, m_currMemory, m_diffMemory;
72  
73    public Profiler() {
74      m_rt = Runtime.getRuntime();
75      m_out = System.out;
76    }
77  
78    /**
79     * Switches the profiler ON and OFF. When OFF all the methods do nothing
80     */
81    public void enable(boolean isEnabled) {
82      m_enabled = isEnabled;
83    } // enable
84  
85    /**
86     * Answers is the profiler switched ON or OFF. When OFF all the methods do
87     * nothing
88     */
89    public boolean isEnabled() {
90      return m_enabled;
91    }
92  
93    /**
94     * Tell's the profiler whether to call the garbage collector when detecting
95     * memory usage or not. If switched ON the GC is called following a
96     * specific strategy as many time as needed so to collect all the memory
97     * that can be collected. This makes the profiling slower and also does not
98     * correctly account for the really memory usage of the applicaton when
99     * run without the profiler. On the other hand, with garbage collection, it
100    * is easier to detecet the amount of memory really necessary for the
101    * application.
102    */
103   public void enableGCCalling(boolean collect) {
104     m_garbageCollection = collect;
105   }
106 
107   /**
108    * @see enableGCCalling(boolean)
109    */
110   public boolean isGCCallingEnabled() {
111     return m_garbageCollection;
112   }
113 
114 
115   /**
116    * Returns the time spend by the profiler during the last run. It is the
117    * case that
118    *      net_run_time = run_duration - profiler_time
119    */
120   public long getProfilerTime() {
121     return m_profilerTime;
122   }
123 
124   /**
125    * Returns the time spend in the last run without the time
126    * spend by the profiler. It is the case that
127    *      net_run_time = run_duration - profiler_time
128    */
129   public long getNetRunTime() {
130     return m_lastCheckTime - m_profilerTime;
131   };
132 
133   /**
134    * Returns the time spend in the current run until the last check-point
135    * inclusive the time spend by the profiler. It is the case that
136    *      net_run_time = run_duration - profiler_time
137    */
138   public long getRunDuration() {
139     return m_lastCheckTime;
140 //        long auxTime1 = (System.currentTimeMillis() - m_startTime1000)/10;
141 //        return ((double)auxTime1)/100;
142   } ;
143 
144 
145   /**
146    * Inialises the profiler for a new run
147    */
148   public void initRun(String runDescription) {
149     m_out.println("-----------------------------------------------");
150     m_out.println("New profiler run: " + runDescription);
151     m_out.println("-----------------------------------------------");
152 
153     m_maxMemory=0;
154     m_currMemory=0;
155     m_diffMemory=0;
156     m_profilerTime=0;
157     m_startTime = System.currentTimeMillis();
158     m_lastCheckTime=0;
159     m_lastDuration=0;
160 
161     m_categorySums = new Hashtable();
162     m_categoryLasts = new Hashtable();
163   } // initRun
164 
165   /**
166    * To be called at all execution points of interest. Detects the time
167    * and memory usage in absolute terms as well as compared to the previous
168    * execution point
169    */
170   public void checkPoint(String execPointDescr) {
171     checkPoint(execPointDescr, new String[0], true, true, true);
172   }
173 
174   /**
175    * In addition to the variant of the method with two parameters allows:
176    * a set of categories (identified by strings) to which the preceeding
177    * fragment of code belongs; flag determining whether the description of
178    * the execution point to be displayed; flag determining whether the
179    * statistics to be shown
180    */
181   public void checkPoint(String execPointDescr, String categories[],
182       boolean showDescr, boolean showStats, boolean memoryCheck)
183   {
184     if (!m_enabled)
185       return;
186 
187     long currTime = System.currentTimeMillis() - m_startTime;
188     m_lastDuration = currTime - m_lastCheckTime;
189 
190     if (memoryCheck) {
191       long oldMemory = m_currMemory;
192 
193       if (m_garbageCollection) {
194         do {
195           m_currMemory = m_rt.totalMemory() - m_rt.freeMemory();
196           m_rt.gc();
197           try {wait(300);} catch (Exception e) {}
198           m_rt.gc();
199         } while (m_currMemory > m_rt.totalMemory() - m_rt.freeMemory());
200       }
201       else {
202         m_currMemory = m_rt.totalMemory() - m_rt.freeMemory();
203       }
204 
205       m_currMemory /= 1000;
206       m_maxMemory = Math.max(m_maxMemory, m_currMemory);
207       m_diffMemory = m_currMemory - oldMemory;
208     } // if (memoryCheck)
209 
210     m_lastCheckTime = System.currentTimeMillis() - m_startTime;
211     m_profilerTime += (m_lastCheckTime - currTime);
212 
213     checkCategories(categories);
214     showResults(execPointDescr, showDescr, showStats);
215   } // checkPoint
216 
217   private void checkCategories(String categs[]) {
218     int size = categs.length;
219     String categ;
220     long last, sum;
221     Long l;
222     for (int i=0; i<size; i++) {
223       categ = categs[i].toUpperCase();
224       l = (Long)m_categorySums.get(categ);
225       sum = (l==null) ? 0 : l.longValue();
226       sum += m_lastDuration;
227       m_categorySums.put(categ, new Long(sum));
228       m_categoryLasts.put(categ, new Long(m_lastDuration));
229     } // for
230   } // checkCategories
231 
232   private void showResults(String execPointDescr, boolean showDescr,
233       boolean showStats)
234   {
235     StringBuffer buff = new StringBuffer(500);
236     if (showDescr) {
237       buff.append("---------LOG: ");
238       buff.append(execPointDescr);
239       buff.append("---------");
240     }
241 
242     if (showStats) {
243       buff.append("\nMemory: ");
244       buff.append(m_currMemory);
245       buff.append("k; change: ");
246       buff.append(m_diffMemory);
247       buff.append("k; max: ");
248       buff.append(m_maxMemory);
249       buff.append("k; Net time:   ");
250       buff.append(printTime(getNetRunTime()));
251       buff.append("; since prev.: ");
252       buff.append(printTime(m_lastDuration));
253 //            buff.append("; profiler time: ");
254 //            buff.append(printTime(m_profilerTime));
255 //            buff.append("; duration: ");
256 //            buff.append(printTime(m_lastCheckTime));
257     }
258 
259     if (buff.length() > 0) {
260         m_out.println(buff.toString());
261     }
262   } // showResults
263 
264   /**
265    * Returns 0 if the category was not found
266    */
267   public long getCategoryTimeSum(String category) {
268     Long sum = (Long)m_categorySums.get(category.toUpperCase());
269     return (sum == null) ? 0 : sum.longValue();
270   } // getCategoryTimeSum
271 
272   /**
273    * Returns 0 if the category was not found
274    */
275   public long getCategoryTimeLast(String category) {
276     Long sum = (Long)m_categoryLasts.get(category.toUpperCase());
277     return (sum == null) ? 0 : sum.longValue();
278   } // getCategoryTimeSum
279 
280   /**
281    * Prints the time for all the categories of activities
282    */
283   public void showCategoryTimes() {
284     m_out.println("Time spent by categories:");
285     Enumeration categNames = m_categorySums.keys();
286     String categ;
287     while (categNames.hasMoreElements()) {
288       categ = (String)categNames.nextElement();
289       showCategoryTime(categ);
290     } // while
291   } // showCategoryTimes
292 
293   /**
294    * Prints the time for certain category of activities
295    */
296   public void showCategoryTime(String categ) {
297     m_out.println(categ + ", sum=" +
298             printTime(getCategoryTimeSum(categ)) +
299             ", last=" + printTime(getCategoryTimeLast(categ)));
300   } // showCategoryTimes
301 
302   /**
303    * An auxiliary routine printing time in "nnn.nns" format
304    */
305   public String printTime(long timeMillis) {
306     long round = timeMillis/1000;
307     long remaind = (timeMillis % 1000)/10;
308     StringBuffer buff = new StringBuffer(10);
309     buff.append(round);
310     buff.append(".");
311     buff.append(remaind);
312     buff.append("s");
313     return buff.toString();
314   } // printTime
315 
316   /**
317    * An auxiliary routine printing in a string speed
318    */
319   public String printSpeed(long timeMillis,
320       double whatever, String whateverMeasure)
321   {
322     double speed1000 = (double) whatever/ timeMillis;
323     long round = (long)((double)speed1000*1000);
324     long remaind =  (long)(((double)speed1000*100000) - 100 * round);
325     StringBuffer buff = new StringBuffer(10);
326     buff.append(round);
327     buff.append(".");
328     buff.append(remaind);
329     buff.append(whateverMeasure);
330     buff.append("/s");
331     return buff.toString();
332   } // printTime
333 
334   /**
335    * An auxiliary routine printing time, avg. time, and avg. speed for
336    * a category
337    */
338   public void printCategAvg(String categ, long items,
339       double volume, String whateverMeasure)
340   {
341     long time = getCategoryTimeSum(categ);
342     if (time==0) {
343       m_out.println("Category \"" + categ + "\" not found");
344     }
345 
346     m_out.println("Category \"" + categ + "\",  Time= " +
347         printTime(time) + "; avg. time= " +
348         printTime(time/items) +  "; speed= " +
349         printSpeed(time, volume, whateverMeasure));
350   } // printCategAvg
351 
352 } // Profiler