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 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 FILE_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 */
50 unsigned 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  */
59 static 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  */
85 static 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  */
98 static 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  */
111 void 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 -
203  profiler->accvar_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;
224  profiler->accvar_msb++;
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  */
242 unsigned 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  */
256 unsigned 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  */
276 unsigned long profile_stddev ( struct profiler *profiler ) {
277 
278  return isqrt ( profile_variance ( profiler ) );
279 }
unsigned long profile_mean(struct profiler *profiler)
Get mean sample value.
Definition: profile.c:242
static unsigned int profile_mean_shift(struct profiler *profiler)
Calculate bit shift for mean sample value.
Definition: profile.c:85
unsigned long isqrt(unsigned long value)
Find integer square root.
Definition: isqrt.c:41
#define DBGC(...)
Definition: compiler.h:505
unsigned long long accvar
Accumulated variance (scaled)
Definition: profile.h:45
#define flsll(x)
Find last (i.e.
Definition: strings.h:149
A data structure for storing profiling information.
Definition: profile.h:27
unsigned long profile_stddev(struct profiler *profiler)
Get sample standard deviation.
Definition: profile.c:276
FILE_SECBOOT(PERMITTED)
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
unsigned int mean_msb
Mean sample value MSB.
Definition: profile.h:43
Assertions.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
unsigned int count
Number of samples.
Definition: profile.h:35
pseudo_bit_t value[0x00020]
Definition: arbel.h:13
Profiling.
Integer square root.
unsigned char uint8_t
Definition: stdint.h:10
unsigned long profile_variance(struct profiler *profiler)
Get sample variance.
Definition: profile.c:256
void profile_update(struct profiler *profiler, unsigned long sample)
Update profiler with a new sample.
Definition: profile.c:111
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition: vsprintf.c:383
unsigned long profile_excluded
Accumulated time excluded from profiling.
Definition: profile.c:50
#define flsl(x)
Find last (i.e.
Definition: strings.h:158
#define INT_MAX
Definition: limits.h:37
unsigned int accvar_msb
Accumulated variance MSB.
Definition: profile.h:51
unsigned long mean
Mean sample value (scaled)
Definition: profile.h:37
static const char * profile_hex_fraction(signed long long value, unsigned int shift)
Format a hex fraction (for debugging)
Definition: profile.c:59
String functions.
static unsigned int profile_accvar_shift(struct profiler *profiler)
Calculate bit shift for accumulated variance value.
Definition: profile.c:98