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) ? 0 : 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() > 0 && 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) ? 0 : 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) ? 0 : 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 = (double) whatever/ timeMillis;
340 long round = (long)((double)speed1000*1000);
341 long remaind = (long)(((double)speed1000*100000) - 100 * 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
|