f20e90cebdd996833bd171adef295fda7d4bb588
[occt.git] / src / DebugTools / Perf.cxx
1 // Copyright (c) 1999-2012 OPEN CASCADE SAS
2 //
3 // The content of this file is subject to the Open CASCADE Technology Public
4 // License Version 6.5 (the "License"). You may not use the content of this file
5 // except in compliance with the License. Please obtain a copy of the License
6 // at http://www.opencascade.org and read it completely before using this file.
7 //
8 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
9 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
10 //
11 // The Original Code and all software distributed under the License is
12 // distributed on an "AS IS" basis, without warranty of any kind, and the
13 // Initial Developer hereby disclaims all such warranties, including without
14 // limitation, any warranties of merchantability, fitness for a particular
15 // purpose or non-infringement. Please see the License for the specific terms
16 // and conditions governing the rights and limitations under the License.
17
18 /*======================================================================
19 File    :       Perf.c
20 Purpose :       Set of functions to measure the CPU user time
21 Author  :       Michael SAZONOV <msv@nnov.matra-dtv.fr>
22 Created :       10/08/2000
23 History :       25/09/2001 : AGV : (const char *) in prototypes;
24                                    search in table using hashvalue
25                 09/11/2001 : AGV : Add functions perf_*_imeter for performance
26                                    Add function perf_tick_meter
27 ======================================================================*/
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <limits.h>
33 #include <DebugTools.h>
34
35 #ifndef WNT
36 #include <unistd.h>
37 #endif
38
39 /* Function times() is more precise than clock() because it does not take into
40    account the system time and the time of child processes                   */
41 #include <time.h>
42 #ifdef WNT
43 #define STRICT
44 #include <windows.h>
45 typedef __int64 PERF_TIME;
46 #define PICK_TIME(_utime) {     \
47   FILETIME t1, t2, ktime;       \
48   GetThreadTimes (GetCurrentThread(), &t1, &t2, &ktime, (FILETIME *)&(_utime));\
49 }
50 #define GET_SECONDS(_utime) (((double)(_utime))/10000000.)
51 #else
52 #include <sys/times.h>
53 typedef clock_t PERF_TIME;
54 #define PICK_TIME(_utime) {     \
55   struct tms tmbuf;             \
56   times (&tmbuf);               \
57   (_utime) = tmbuf.tms_utime;   \
58 }
59
60 #ifndef CLK_TCK
61 #define CLK_TCK 100             /* as SunOS */
62 #endif
63
64 #define GET_SECONDS(_utime) (((double)(_utime))/CLK_TCK)
65 #endif
66 /*======================================================================
67         DEFINITIONS
68 ======================================================================*/
69
70 typedef struct {
71   char*         name;           /* identifier */
72   unsigned int  hash;           /* hash value */
73   PERF_TIME     cumul_time;     /* cumulative time */
74   PERF_TIME     start_time;     /* to store start time */
75   int           nb_enter;       /* number of enters */
76 } t_TimeCounter;
77
78 #define MAX_METERS 100
79
80 static t_TimeCounter MeterTable[MAX_METERS];
81 static int nb_meters = 0;
82
83 static t_TimeCounter*   find_meter       (const char * const MeterName);
84 static t_TimeCounter*   _perf_init_meter (const char * const MeterName,
85                                           const int    doFind);
86 static unsigned int     hash_value       (const char * const aString);
87
88 /*======================================================================
89 Function :      perf_init_meter
90 Purpose  :      Creates new counter (if it is absent) identified by
91                 MeterName and resets its cumulative value
92 Returns  :      iMeter if OK, 0 if alloc problem
93 ======================================================================*/
94 int perf_init_meter (const char * const MeterName)
95 {
96   t_TimeCounter* ptc = _perf_init_meter (MeterName, ~0);
97   return (ptc ? (ptc-MeterTable)+1 : 0);
98 }
99
100 /*======================================================================
101 Function :      perf_tick_meter
102 Purpose  :      Increments the counter of meter MeterName without changing
103                 its state with respect to measurement of time.
104                 creates new meter if there is no such meter
105 Returns  :      iMeter if OK, 0 if no such meter and cannot create a new one
106 ======================================================================*/
107 int perf_tick_meter (const char * const MeterName)
108 {
109   t_TimeCounter* ptc = find_meter (MeterName);
110
111   if (!ptc) {
112     /* create new meter */
113     ptc = _perf_init_meter (MeterName, 0);
114   }
115
116   if (ptc) {
117     ptc -> nb_enter ++;
118     return (ptc-MeterTable) + 1;
119   }
120   return 0;
121 }
122
123 /*======================================================================
124 Function :      perf_tick_imeter
125 Purpose  :      Increments the counter of meter iMeter without changing
126                 its state with respect to measurement of time.
127 Returns  :      iMeter if OK, 0 if no such meter
128 ======================================================================*/
129 int perf_tick_imeter (const int iMeter)
130 {
131   if (iMeter > 0 && iMeter <= nb_meters) {
132     MeterTable[iMeter-1].nb_enter ++;
133     return iMeter;
134   }
135   return 0;
136 }
137
138 /*======================================================================
139 Function :      perf_start_meter
140 Purpose  :      Forces meter MeterName to begin to count by remembering
141                 the current data of timer;
142                 creates new meter if there is no such meter
143 Returns  :      iMeter if OK, 0 if no such meter and cannot create a new one
144 ======================================================================*/
145 int perf_start_meter (const char * const MeterName)
146 {
147   t_TimeCounter* ptc = find_meter (MeterName);
148
149   if (!ptc) {
150     /* create new meter */
151     ptc = _perf_init_meter (MeterName, 0);
152   }
153
154   if (ptc) {
155     PICK_TIME (ptc->start_time)
156     return (ptc - MeterTable) + 1;
157   }
158
159   return 0;
160 }
161
162 /*======================================================================
163 Function :      perf_start_imeter
164 Purpose  :      Forces meter with number iMeter to begin count by remembering
165                 the current data of timer;
166                 the meter must be previously created
167 Returns  :      iMeter if OK, 0 if no such meter
168 ======================================================================*/
169 int perf_start_imeter (const int iMeter)
170 {
171   if (iMeter > 0 && iMeter <= nb_meters) {
172     t_TimeCounter * const ptc = &MeterTable[iMeter-1];
173     PICK_TIME (ptc->start_time)
174     return iMeter;
175   }
176   return 0;
177 }
178
179 /*======================================================================
180 Function :      perf_stop_meter
181 Purpose  :      Forces meter MeterName to stop and cumulate time elapsed
182                 since start
183 Returns  :      1 if OK, 0 if no such meter or it is has not been started
184 ======================================================================*/
185 int perf_stop_meter (const char * const MeterName)
186 {
187   t_TimeCounter* ptc = find_meter (MeterName);
188
189   if (ptc && ptc->start_time) {
190     PERF_TIME utime;
191     PICK_TIME (utime)
192     ptc->cumul_time += utime - ptc->start_time;
193     ptc->start_time = 0;
194     ptc->nb_enter++;
195     return (ptc-MeterTable) + 1;
196   }
197
198   return 0;
199 }
200
201 /*======================================================================
202 Function :      perf_stop_imeter
203 Purpose  :      Forces meter with number iMeter to stop and cumulate the time
204                 elapsed since the start
205 Returns  :      iMeter if OK, 0 if no such meter
206 ======================================================================*/
207 int perf_stop_imeter (const int iMeter)
208 {
209   if (iMeter > 0 && iMeter <= nb_meters) {
210     t_TimeCounter * const ptc = &MeterTable[iMeter-1];
211
212     if (ptc->start_time) {
213       PERF_TIME utime;
214       PICK_TIME (utime)
215       ptc->cumul_time += utime - ptc->start_time;
216       ptc->start_time = 0;
217       ptc->nb_enter++;
218       return iMeter;
219     }
220   }
221   return 0;
222 }
223
224 /*======================================================================
225 Function :      perf_get_meter
226 Purpose  :      Tells the time cumulated by meter MeterName and the number
227                 of enters to this meter
228 Output   :      *nb_enter, *seconds if the pointers != NULL
229 Returns  :      iMeter if OK, 0 if no such meter
230 ======================================================================*/
231 int perf_get_meter (const char  * const MeterName,
232                     int         * nb_enter,
233                     double      * seconds)
234 {
235   t_TimeCounter* ptc = find_meter (MeterName);
236
237   if (!ptc) return 0;
238
239   if (nb_enter) *nb_enter = ptc->nb_enter;
240   if (seconds) *seconds = GET_SECONDS(ptc->cumul_time);
241   return (ptc-MeterTable) + 1;
242 }
243
244 /*======================================================================
245 Function :      perf_print_all_meters
246 Purpose  :      Prints on stdout the cumulated time and the number of
247                 enters for each meter in MeterTable;
248                 resets all meters
249 Output   :      none
250 Returns  :      none
251 ======================================================================*/
252 void perf_print_all_meters (void)
253 {
254   int i;
255
256   if (!nb_meters) return;
257
258   printf ("     Perf meter results                  :   enters   seconds  enters/sec\n");
259
260   for (i=0; i<nb_meters; i++) {
261     t_TimeCounter* ptc = &MeterTable[i];
262
263     if (ptc && ptc->nb_enter) {
264       double secs = GET_SECONDS(ptc->cumul_time);
265
266       if (ptc->start_time)
267         printf ("Warning : meter %s has not been stopped\n", ptc->name);
268
269       printf ("%-40s : %7d %8.2f %10.2f\n",
270               ptc->name, ptc->nb_enter, secs,
271               (secs>0. ? ptc->nb_enter / secs : 0.));
272
273       ptc->cumul_time = 0;
274       ptc->start_time = 0;
275       ptc->nb_enter   = 0;
276     }
277   }
278 }
279
280 /*======================================================================
281 Function :      perf_close_meter
282 Purpose  :      Prints out a meter and resets it
283 Returns  :      none
284 ======================================================================*/
285 void perf_close_meter (const char * const MeterName)
286 {
287   t_TimeCounter* ptc = find_meter (MeterName);
288   if (ptc && ptc->nb_enter) {
289     if (ptc->start_time)
290       printf ("  ===> Warning : meter %s has not been stopped\n", ptc->name);
291     printf ("  ===> [%s] : %d enters, %9.3f seconds\n",
292             ptc->name, ptc->nb_enter, GET_SECONDS(ptc->cumul_time));
293     ptc->cumul_time = 0;
294     ptc->start_time = 0;
295     ptc->nb_enter   = 0;
296   }
297 }
298
299 /*======================================================================
300 Function :      perf_destroy_all_meters
301 Purpose  :      Deletes all meters and frees memory
302 Returns  :      none
303 ======================================================================*/
304 void perf_destroy_all_meters (void)
305 {
306   int i;
307   for (i=0; i<nb_meters; i++)
308     free (MeterTable[i].name);
309   nb_meters = 0;
310 }
311
312 /* agv - non portable: #pragma fini (perf_print_and_destroy) */
313
314 void perf_print_and_destroy (void)
315 {
316   perf_print_all_meters ();
317   perf_destroy_all_meters ();
318 }
319
320 /*======================================================================
321 Function :      _perf_init_meter
322 Purpose  :      Creates new counter (if it is absent) identified by
323                 MeterName and resets its cumulative value
324 Returns  :      the pointer if OK, 0 if alloc problem
325 Remarks  :      For internal use in this module
326 ======================================================================*/
327 static t_TimeCounter* _perf_init_meter (const char * const MeterName,
328                                         const int  doFind)
329 {
330   static int hasbeencalled = 0;
331   t_TimeCounter* ptc = 0;
332   if (doFind)
333     ptc = find_meter (MeterName);
334
335   if (!ptc) {
336     if (nb_meters >= MAX_METERS) return 0;
337     ptc = &MeterTable[nb_meters];
338
339     ptc -> name = strdup (MeterName);
340     if (!ptc -> name)
341       return 0;
342
343     ptc -> hash = hash_value (MeterName);
344     nb_meters++;
345   }
346
347   ptc->cumul_time = 0;
348   ptc->start_time = 0;
349   ptc->nb_enter   = 0;
350   if (hasbeencalled == 0) {
351     /*atexit (perf_print_and_destroy);*/
352     hasbeencalled = ~0;
353   }
354   return ptc;
355 }
356
357 /*======================================================================
358 Function :      find_meter
359 Purpose  :      Finds the meter MeterName in the MeterTable
360 Returns  :      Pointer to meter object
361 Remarks  :      For internal use in this module
362 ======================================================================*/
363 static t_TimeCounter* find_meter (const char * const MeterName)
364 {
365   int i;
366   const unsigned int aHash = hash_value (MeterName);
367   for (i=0; i<nb_meters; i++)
368     if (MeterTable[i].hash == aHash)
369       if (!strcmp (MeterTable[i].name, MeterName)) return &MeterTable[i];
370   return NULL;
371 }
372
373 static const unsigned int wCRC16a[16] =
374 {
375     0000000,    0140301,    0140601,    0000500,
376     0141401,    0001700,    0001200,    0141101,
377     0143001,    0003300,    0003600,    0143501,
378     0002400,    0142701,    0142201,    0002100,
379 };
380
381 static const unsigned int wCRC16b[16] =
382 {
383     0000000,    0146001,    0154001,    0012000,
384     0170001,    0036000,    0024000,    0162001,
385     0120001,    0066000,    0074000,    0132001,
386     0050000,    0116001,    0104001,    0043000,
387 };
388
389 /*======================================================================
390 Function :      hash_value
391 Returns  :      the hash value of the string
392 Remarks  :      For internal use in this module
393 ======================================================================*/
394 static unsigned int hash_value (const char * const aString)
395 {
396   int i;
397   unsigned int aCRC = 0;
398   const int aLen = strlen (aString);
399   const unsigned char * aPtr = (const unsigned char *) aString;
400   for (i = aLen; i > 0; i--) {
401     const unsigned int  bTmp = aCRC ^ (const unsigned int) (* aPtr++);
402     aCRC = ((aCRC >> 8) ^ wCRC16a[bTmp & 0x0F]) ^ wCRC16b[(bTmp >> 4) & 0x0F];
403   }
404   return aCRC;
405 }