iPXE
profile.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_SECBOOT ( PERMITTED );
26
27#include <stdint.h>
28#include <stdio.h>
29#include <strings.h>
30#include <limits.h>
31#include <assert.h>
32#include <ipxe/isqrt.h>
33#include <ipxe/profile.h>
34
35/** @file
36 *
37 * Profiling
38 *
39 * The profiler computes basic statistics (mean, variance, and
40 * standard deviation) for the samples which it records. Note that
41 * these statistics need not be completely accurate; it is sufficient
42 * to give a rough approximation.
43 *
44 * The algorithm for updating the mean and variance estimators is from
45 * The Art of Computer Programming (via Wikipedia), with adjustments
46 * to avoid the use of floating-point instructions.
47 */
48
49/** Accumulated time excluded from profiling */
50unsigned long profile_excluded;
51
52/**
53 * Format a hex fraction (for debugging)
54 *
55 * @v value Value
56 * @v shift Bit shift
57 * @ret string Formatted hex fraction
58 */
59static const char * profile_hex_fraction ( signed long long value,
60 unsigned int shift ) {
61 static char buf[23] = "-"; /* -0xXXXXXXXXXXXXXXXX.XX + NUL */
62 unsigned long long int_part;
63 uint8_t frac_part;
64 char *ptr;
65
66 if ( value < 0 ) {
67 value = -value;
68 ptr = &buf[0];
69 } else {
70 ptr = &buf[1];
71 }
72 int_part = ( value >> shift );
73 frac_part = ( value >> ( shift - ( 8 * sizeof ( frac_part ) ) ) );
74 snprintf ( &buf[1], ( sizeof ( buf ) - 1 ), "%#llx.%02x",
75 int_part, frac_part );
76 return ptr;
77}
78
79/**
80 * Calculate bit shift for mean sample value
81 *
82 * @v profiler Profiler
83 * @ret shift Bit shift
84 */
85static inline unsigned int profile_mean_shift ( struct profiler *profiler ) {
86
87 return ( ( ( 8 * sizeof ( profiler->mean ) ) - 1 ) /* MSB */
88 - 1 /* Leave sign bit unused */
89 - profiler->mean_msb );
90}
91
92/**
93 * Calculate bit shift for accumulated variance value
94 *
95 * @v profiler Profiler
96 * @ret shift Bit shift
97 */
98static inline unsigned int profile_accvar_shift ( struct profiler *profiler ) {
99
100 return ( ( ( 8 * sizeof ( profiler->accvar ) ) - 1 ) /* MSB */
101 - 1 /* Leave top bit unused */
102 - profiler->accvar_msb );
103}
104
105/**
106 * Update profiler with a new sample
107 *
108 * @v profiler Profiler
109 * @v sample Sample value
110 */
111void profile_update ( struct profiler *profiler, unsigned long sample ) {
112 unsigned int sample_msb;
113 unsigned int mean_shift;
114 unsigned int delta_shift;
115 signed long pre_delta;
116 signed long post_delta;
117 signed long long accvar_delta;
118 unsigned int accvar_delta_shift;
119 unsigned int accvar_delta_msb;
120 unsigned int accvar_shift;
121
122 /* Our scaling logic assumes that sample values never overflow
123 * a signed long (i.e. that the high bit is always zero).
124 */
125 assert ( ( ( signed ) sample ) >= 0 );
126
127 /* Update sample count, limiting to avoid signed overflow */
128 if ( profiler->count < INT_MAX )
129 profiler->count++;
130
131 /* Adjust mean sample value scale if necessary. Skip if
132 * sample is zero (in which case flsl(sample)-1 would
133 * underflow): in the case of a zero sample we have no need to
134 * adjust the scale anyway.
135 */
136 if ( sample ) {
137 sample_msb = ( flsl ( sample ) - 1 );
138 if ( profiler->mean_msb < sample_msb ) {
139 profiler->mean >>= ( sample_msb - profiler->mean_msb );
140 profiler->mean_msb = sample_msb;
141 }
142 }
143
144 /* Scale sample to internal units */
145 mean_shift = profile_mean_shift ( profiler );
146 sample <<= mean_shift;
147
148 /* Update mean */
149 pre_delta = ( sample - profiler->mean );
150 profiler->mean += ( pre_delta / ( ( signed ) profiler->count ) );
151 post_delta = ( sample - profiler->mean );
152 delta_shift = mean_shift;
153 DBGC ( profiler, "PROFILER %p sample %#lx mean %s", profiler,
154 ( sample >> mean_shift ),
155 profile_hex_fraction ( profiler->mean, mean_shift ) );
156 DBGC ( profiler, " pre %s",
157 profile_hex_fraction ( pre_delta, delta_shift ) );
158 DBGC ( profiler, " post %s\n",
159 profile_hex_fraction ( post_delta, delta_shift ) );
160
161 /* Scale both deltas to fit in half of an unsigned long long
162 * to avoid potential overflow on multiplication. Note that
163 * shifting a signed quantity is "implementation-defined"
164 * behaviour in the C standard, but gcc documents that it will
165 * always perform sign extension.
166 */
167 if ( sizeof ( pre_delta ) > ( sizeof ( accvar_delta ) / 2 ) ) {
168 unsigned int shift = ( 8 * ( sizeof ( pre_delta ) -
169 ( sizeof ( accvar_delta ) / 2 ) ));
170 pre_delta >>= shift;
171 post_delta >>= shift;
172 delta_shift -= shift;
173 }
174
175 /* Update variance, if applicable. Skip if either delta is
176 * zero (in which case flsl(delta)-1 would underflow): in the
177 * case of a zero delta there is no change to the accumulated
178 * variance anyway.
179 */
180 if ( pre_delta && post_delta ) {
181
182 /* Calculate variance delta */
183 accvar_delta = ( ( ( signed long long ) pre_delta ) *
184 ( ( signed long long ) post_delta ) );
185 accvar_delta_shift = ( 2 * delta_shift );
186 assert ( accvar_delta > 0 );
187
188 /* Calculate variance delta MSB, using flsl() on each
189 * delta individually to provide an upper bound rather
190 * than requiring the existence of flsll().
191 */
192 accvar_delta_msb = ( flsll ( accvar_delta ) - 1 );
193 if ( accvar_delta_msb > accvar_delta_shift ) {
194 accvar_delta_msb -= accvar_delta_shift;
195 } else {
196 accvar_delta_msb = 0;
197 }
198
199 /* Adjust scales as necessary */
200 if ( profiler->accvar_msb < accvar_delta_msb ) {
201 /* Rescale accumulated variance */
202 profiler->accvar >>= ( accvar_delta_msb -
204 profiler->accvar_msb = accvar_delta_msb;
205 } else {
206 /* Rescale variance delta */
207 accvar_delta >>= ( profiler->accvar_msb -
208 accvar_delta_msb );
209 accvar_delta_shift -= ( profiler->accvar_msb -
210 accvar_delta_msb );
211 }
212
213 /* Scale delta to internal units */
214 accvar_shift = profile_accvar_shift ( profiler );
215 accvar_delta <<= ( accvar_shift - accvar_delta_shift );
216
217 /* Accumulate variance */
218 profiler->accvar += accvar_delta;
219
220 /* Adjust scale if necessary */
221 if ( profiler->accvar &
222 ( 1ULL << ( ( 8 * sizeof ( profiler->accvar ) ) - 1 ) ) ) {
223 profiler->accvar >>= 1;
225 accvar_delta >>= 1;
226 accvar_shift--;
227 }
228
229 DBGC ( profiler, "PROFILER %p accvar %s", profiler,
230 profile_hex_fraction ( profiler->accvar, accvar_shift ));
231 DBGC ( profiler, " delta %s\n",
232 profile_hex_fraction ( accvar_delta, accvar_shift ) );
233 }
234}
235
236/**
237 * Get mean sample value
238 *
239 * @v profiler Profiler
240 * @ret mean Mean sample value
241 */
242unsigned long profile_mean ( struct profiler *profiler ) {
243 unsigned int mean_shift = profile_mean_shift ( profiler );
244
245 /* Round to nearest and scale down to original units */
246 return ( ( profiler->mean + ( 1UL << ( mean_shift - 1 ) ) )
247 >> mean_shift );
248}
249
250/**
251 * Get sample variance
252 *
253 * @v profiler Profiler
254 * @ret variance Sample variance
255 */
256unsigned long profile_variance ( struct profiler *profiler ) {
257 unsigned int accvar_shift = profile_accvar_shift ( profiler );
258
259 /* Variance is zero if fewer than two samples exist (avoiding
260 * division by zero error).
261 */
262 if ( profiler->count < 2 )
263 return 0;
264
265 /* Calculate variance, round to nearest, and scale to original units */
266 return ( ( ( profiler->accvar / ( profiler->count - 1 ) )
267 + ( 1ULL << ( accvar_shift - 1 ) ) ) >> accvar_shift );
268}
269
270/**
271 * Get sample standard deviation
272 *
273 * @v profiler Profiler
274 * @ret stddev Sample standard deviation
275 */
276unsigned long profile_stddev ( struct profiler *profiler ) {
277
278 return isqrt ( profile_variance ( profiler ) );
279}
pseudo_bit_t value[0x00020]
Definition arbel.h:2
unsigned char uint8_t
Definition stdint.h:10
Assertions.
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
#define DBGC(...)
Definition compiler.h:505
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
Profiling.
String functions.
#define flsll(x)
Find last (i.e.
Definition strings.h:149
#define flsl(x)
Find last (i.e.
Definition strings.h:158
unsigned long isqrt(unsigned long value)
Find integer square root.
Definition isqrt.c:41
Integer square root.
#define INT_MAX
Definition limits.h:30
static unsigned int profile_accvar_shift(struct profiler *profiler)
Calculate bit shift for accumulated variance value.
Definition profile.c:98
unsigned long profile_mean(struct profiler *profiler)
Get mean sample value.
Definition profile.c:242
unsigned long profile_variance(struct profiler *profiler)
Get sample variance.
Definition profile.c:256
static const char * profile_hex_fraction(signed long long value, unsigned int shift)
Format a hex fraction (for debugging)
Definition profile.c:59
static unsigned int profile_mean_shift(struct profiler *profiler)
Calculate bit shift for mean sample value.
Definition profile.c:85
unsigned long profile_stddev(struct profiler *profiler)
Get sample standard deviation.
Definition profile.c:276
void profile_update(struct profiler *profiler, unsigned long sample)
Update profiler with a new sample.
Definition profile.c:111
unsigned long profile_excluded
Accumulated time excluded from profiling.
Definition profile.c:50
A data structure for storing profiling information.
Definition profile.h:27
unsigned int count
Number of samples.
Definition profile.h:35
unsigned long mean
Mean sample value (scaled)
Definition profile.h:37
unsigned long long accvar
Accumulated variance (scaled)
Definition profile.h:45
unsigned int mean_msb
Mean sample value MSB.
Definition profile.h:43
unsigned int accvar_msb
Accumulated variance MSB.
Definition profile.h:51
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition vsprintf.c:383