initial implementation
[mirror/winof/.git] / core / complib / cl_perf.c
1 /*\r
2  * Copyright (c) 2005 SilverStorm Technologies.  All rights reserved.\r
3  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. \r
4  *\r
5  * This software is available to you under the OpenIB.org BSD license\r
6  * below:\r
7  *\r
8  *     Redistribution and use in source and binary forms, with or\r
9  *     without modification, are permitted provided that the following\r
10  *     conditions are met:\r
11  *\r
12  *      - Redistributions of source code must retain the above\r
13  *        copyright notice, this list of conditions and the following\r
14  *        disclaimer.\r
15  *\r
16  *      - Redistributions in binary form must reproduce the above\r
17  *        copyright notice, this list of conditions and the following\r
18  *        disclaimer in the documentation and/or other materials\r
19  *        provided with the distribution.\r
20  *\r
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
22  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
24  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\r
25  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\r
26  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
27  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
28  * SOFTWARE.\r
29  *\r
30  * $Id:$\r
31  */\r
32 \r
33 \r
34 /*\r
35  * Abstract:\r
36  *      Implementation of performance tracking.\r
37  *\r
38  * Environment:\r
39  *      All supported environments.\r
40  *\r
41  * $Revision$\r
42  */\r
43 \r
44 \r
45 /*\r
46  * Always turn on performance tracking when building this file to allow the\r
47  * performance counter functions to be built into the component library.\r
48  * Users control their use of the functions by defining the PERF_TRACK_ON\r
49  * keyword themselves before including cl_perf.h to enable the macros to\r
50  * resolve to the internal functions.\r
51  */\r
52 #define PERF_TRACK_ON\r
53 \r
54 #include <complib/cl_perf.h>\r
55 #include <complib/cl_debug.h>\r
56 #include <complib/cl_memory.h>\r
57 \r
58 \r
59 \r
60 uint64_t\r
61 __cl_perf_run_calibration(\r
62         IN      cl_perf_t* const p_perf );\r
63 \r
64 \r
65 /*\r
66  * Initialize the state of the performance tracker.\r
67  */\r
68 void\r
69 __cl_perf_construct(\r
70         IN      cl_perf_t* const        p_perf )\r
71 {\r
72         cl_memclr( p_perf, sizeof(cl_perf_t) );\r
73         p_perf->state = CL_UNINITIALIZED;\r
74 }\r
75 \r
76 \r
77 /*\r
78  * Initialize the performance tracker.\r
79  */\r
80 cl_status_t\r
81 __cl_perf_init(\r
82         IN      cl_perf_t* const        p_perf,\r
83         IN      const uintn_t           num_counters )\r
84 {\r
85         cl_status_t             status;\r
86         cl_spinlock_t   lock;\r
87         uintn_t                 i;\r
88         static uint64_t locked_calibration_time = 0;\r
89         static uint64_t normal_calibration_time;\r
90 \r
91         CL_ASSERT( p_perf );\r
92         CL_ASSERT( !p_perf->size && num_counters );\r
93 \r
94         /* Construct the performance tracker. */\r
95         __cl_perf_construct( p_perf );\r
96 \r
97         /* Allocate an array of counters. */\r
98         p_perf->size = num_counters;\r
99         p_perf->data_array = (cl_perf_data_t*)\r
100                 cl_zalloc( sizeof(cl_perf_data_t) * num_counters );\r
101 \r
102         if( !p_perf->data_array )\r
103                 return( CL_INSUFFICIENT_MEMORY );\r
104 \r
105         /* Initialize the user's counters. */\r
106         for( i = 0; i < num_counters; i++ )\r
107         {\r
108                 p_perf->data_array[i].min_time = ((uint64_t)~0);\r
109                 cl_spinlock_construct( &p_perf->data_array[i].lock );\r
110         }\r
111 \r
112         for( i  = 0; i < num_counters; i++ )\r
113         {\r
114                 status = cl_spinlock_init( &p_perf->data_array[i].lock );\r
115                 if( status != CL_SUCCESS )\r
116                 {\r
117                         __cl_perf_destroy( p_perf, FALSE );\r
118                         return( status );\r
119                 }\r
120         }\r
121 \r
122         /*\r
123          * Run the calibration only if it has not been run yet.  Subsequent\r
124          * calls will use the results from the first calibration.\r
125          */\r
126         if( !locked_calibration_time )\r
127         {\r
128                 /*\r
129                  * Perform the calibration under lock to prevent thread context\r
130                  * switches.\r
131                  */\r
132                 cl_spinlock_construct( &lock );\r
133                 status = cl_spinlock_init( &lock );\r
134                 if( status != CL_SUCCESS )\r
135                 {\r
136                         __cl_perf_destroy( p_perf, FALSE );\r
137                         return( status );\r
138                 }\r
139 \r
140                 /* Measure the impact when running at elevated thread priority. */\r
141                 cl_spinlock_acquire( &lock );\r
142                 locked_calibration_time = __cl_perf_run_calibration( p_perf );\r
143                 cl_spinlock_release( &lock );\r
144                 cl_spinlock_destroy( &lock );\r
145 \r
146                 /* Measure the impact when runnin at normal thread priority. */\r
147                 normal_calibration_time = __cl_perf_run_calibration( p_perf );\r
148         }\r
149 \r
150         /* Reset the user's performance counter. */\r
151         p_perf->normal_calibration_time = locked_calibration_time;\r
152         p_perf->locked_calibration_time = normal_calibration_time;\r
153         p_perf->data_array[0].count = 0;\r
154         p_perf->data_array[0].total_time = 0;\r
155         p_perf->data_array[0].min_time = ((uint64_t)~0);\r
156 \r
157         p_perf->state = CL_INITIALIZED;\r
158 \r
159         return( CL_SUCCESS );\r
160 }\r
161 \r
162 \r
163 /*\r
164  * Measure the time to take performance counters.\r
165  */\r
166 uint64_t\r
167 __cl_perf_run_calibration(\r
168         IN      cl_perf_t* const        p_perf )\r
169 {\r
170         uint64_t                start_time;\r
171         uintn_t                 i;\r
172         PERF_DECLARE( 0 );\r
173 \r
174         /* Start timing. */\r
175         start_time = cl_get_time_stamp();\r
176 \r
177         /*\r
178          * Get the performance counter repeatedly in a loop.  Use the first\r
179          * user counter as our test counter.\r
180          */\r
181         for( i = 0; i < PERF_CALIBRATION_TESTS; i++ )\r
182         {\r
183                 cl_perf_start( 0 );\r
184                 cl_perf_stop( p_perf, 0 );\r
185         }\r
186 \r
187         /* Calculate the total time for the calibration. */\r
188         return( cl_get_time_stamp() - start_time );\r
189 }\r
190 \r
191 \r
192 /*\r
193  * Destroy the performance tracker.\r
194  */\r
195 void\r
196 __cl_perf_destroy(\r
197         IN      cl_perf_t* const        p_perf,\r
198         IN      const boolean_t         display )\r
199 {\r
200         uintn_t i;\r
201 \r
202         CL_ASSERT( cl_is_state_valid( p_perf->state ) );\r
203 \r
204         if( !p_perf->data_array )\r
205                 return;\r
206 \r
207         /* Display the performance data as requested. */\r
208         if( display && p_perf->state == CL_INITIALIZED )\r
209                 __cl_perf_display( p_perf );\r
210 \r
211         /* Destroy the user's counters. */\r
212         for( i = 0; i < p_perf->size; i++ )\r
213                 cl_spinlock_destroy( &p_perf->data_array[i].lock );\r
214 \r
215         cl_free( p_perf->data_array );\r
216         p_perf->data_array = NULL;\r
217 \r
218         p_perf->state = CL_UNINITIALIZED;\r
219 }\r
220 \r
221 \r
222 /*\r
223  * Reset the performance counters.\r
224  */\r
225 void\r
226 __cl_perf_reset(\r
227         IN      cl_perf_t* const                p_perf )\r
228 {\r
229         uintn_t i;\r
230 \r
231         for( i = 0; i < p_perf->size; i++ )\r
232         {\r
233                 cl_spinlock_acquire( &p_perf->data_array[i].lock );\r
234                 p_perf->data_array[i].min_time = ((uint64_t)~0);\r
235                 p_perf->data_array[i].total_time = 0;\r
236                 p_perf->data_array[i].count = 0;\r
237                 cl_spinlock_release( &p_perf->data_array[i].lock );\r
238         }\r
239 }\r
240 \r
241 \r
242 /*\r
243  * Display the captured performance data.\r
244  */\r
245 void\r
246 __cl_perf_display(\r
247         IN      const cl_perf_t* const  p_perf )\r
248 {\r
249         uintn_t i;\r
250 \r
251         CL_ASSERT( p_perf );\r
252         CL_ASSERT( p_perf->state == CL_INITIALIZED );\r
253 \r
254         cl_msg_out( "\n\n\nCL Perf:\tPerformance Data\n" );\r
255 \r
256         cl_msg_out( "CL Perf:\tCounter Calibration Time\n" );\r
257         cl_msg_out( "CL Perf:\tLocked TotalTime\tNormal TotalTime\tTest Count\n" );\r
258         cl_msg_out( "CL Perf:\t%"PRIu64"\t%"PRIu64"\t%u\n",\r
259                 p_perf->locked_calibration_time, p_perf->normal_calibration_time,\r
260                 PERF_CALIBRATION_TESTS );\r
261 \r
262         cl_msg_out( "CL Perf:\tUser Performance Counters\n" );\r
263         cl_msg_out( "CL Perf:\tIndex\tTotalTime\tMinTime\tCount\n" );\r
264         for( i = 0; i < p_perf->size; i++ )\r
265         {\r
266                 cl_msg_out( "CL Perf:\t%lu\t%"PRIu64"\t%"PRIu64"\t%"PRIu64"\n",\r
267                         i, p_perf->data_array[i].total_time,\r
268                         p_perf->data_array[i].min_time, p_perf->data_array[i].count );\r
269         }\r
270         cl_msg_out( "CL Perf:\tEnd of User Performance Counters\n" );\r
271 }\r