Переглянути джерело

[profile] Add generic profiling infrastructure

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 10 роки тому
джерело
коміт
e5f6a9be38

+ 28
- 0
src/arch/i386/include/bits/profile.h Переглянути файл

@@ -0,0 +1,28 @@
1
+#ifndef _BITS_PROFILE_H
2
+#define _BITS_PROFILE_H
3
+
4
+/** @file
5
+ *
6
+ * Profiling
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+
14
+/**
15
+ * Get profiling timestamp
16
+ *
17
+ * @ret timestamp	Timestamp
18
+ */
19
+static inline __attribute__ (( always_inline )) uint64_t
20
+profile_timestamp ( void ) {
21
+	uint64_t tsc;
22
+
23
+	/* Read timestamp counter */
24
+	__asm__ __volatile__ ( "rdtsc" : "=A" ( tsc ) );
25
+	return tsc;
26
+}
27
+
28
+#endif /* _BITS_PROFILE_H */

+ 29
- 0
src/arch/x86_64/include/bits/profile.h Переглянути файл

@@ -0,0 +1,29 @@
1
+#ifndef _BITS_PROFILE_H
2
+#define _BITS_PROFILE_H
3
+
4
+/** @file
5
+ *
6
+ * Profiling
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+
14
+/**
15
+ * Get profiling timestamp
16
+ *
17
+ * @ret timestamp	Timestamp
18
+ */
19
+static inline __attribute__ (( always_inline )) uint64_t
20
+profile_timestamp ( void ) {
21
+	uint32_t eax;
22
+	uint32_t edx;
23
+
24
+	/* Read timestamp counter */
25
+	__asm__ __volatile__ ( "rdtsc" : "=a" ( eax ), "=d" ( edx ) );
26
+	return ( ( ( ( uint64_t ) edx ) << 32 ) | eax );
27
+}
28
+
29
+#endif /* _BITS_PROFILE_H */

+ 269
- 0
src/core/profile.c Переглянути файл

@@ -0,0 +1,269 @@
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
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+#include <stdint.h>
23
+#include <stdio.h>
24
+#include <strings.h>
25
+#include <assert.h>
26
+#include <ipxe/isqrt.h>
27
+#include <ipxe/profile.h>
28
+
29
+/** @file
30
+ *
31
+ * Profiling
32
+ *
33
+ * The profiler computes basic statistics (mean, variance, and
34
+ * standard deviation) for the samples which it records.  Note that
35
+ * these statistics need not be completely accurate; it is sufficient
36
+ * to give a rough approximation.
37
+ *
38
+ * The algorithm for updating the mean and variance estimators is from
39
+ * The Art of Computer Programming (via Wikipedia), with adjustments
40
+ * to avoid the use of floating-point instructions.
41
+ */
42
+
43
+/**
44
+ * Format a hex fraction (for debugging)
45
+ *
46
+ * @v value		Value
47
+ * @v shift		Bit shift
48
+ * @ret string		Formatted hex fraction
49
+ */
50
+static const char * profile_hex_fraction ( signed long long value,
51
+					   unsigned int shift ) {
52
+	static char buf[23] = "-"; /* -0xXXXXXXXXXXXXXXXX.XX + NUL */
53
+	unsigned long long int_part;
54
+	uint8_t frac_part;
55
+	char *ptr;
56
+
57
+	if ( value < 0 ) {
58
+		value = -value;
59
+		ptr = &buf[0];
60
+	} else {
61
+		ptr = &buf[1];
62
+	}
63
+	int_part = ( value >> shift );
64
+	frac_part = ( value >> ( shift - ( 8 * sizeof ( frac_part ) ) ) );
65
+	snprintf ( &buf[1], ( sizeof ( buf ) - 1  ), "%#llx.%02x",
66
+		   int_part, frac_part );
67
+	return ptr;
68
+}
69
+
70
+/**
71
+ * Calculate bit shift for mean sample value
72
+ *
73
+ * @v profiler		Profiler
74
+ * @ret shift		Bit shift
75
+ */
76
+static inline unsigned int profile_mean_shift ( struct profiler *profiler ) {
77
+
78
+	return ( ( ( 8 * sizeof ( profiler->mean ) ) - 1 ) /* MSB */
79
+		 - 1 /* Leave sign bit unused */
80
+		 - profiler->mean_msb );
81
+}
82
+
83
+/**
84
+ * Calculate bit shift for accumulated variance value
85
+ *
86
+ * @v profiler		Profiler
87
+ * @ret shift		Bit shift
88
+ */
89
+static inline unsigned int profile_accvar_shift ( struct profiler *profiler ) {
90
+
91
+	return ( ( ( 8 * sizeof ( profiler->accvar ) ) - 1 ) /* MSB */
92
+		 - 1 /* Leave top bit unused */
93
+		 - profiler->accvar_msb );
94
+}
95
+
96
+/**
97
+ * Update profiler with a new sample
98
+ *
99
+ * @v profiler		Profiler
100
+ * @v sample		Sample value
101
+ */
102
+void profile_update ( struct profiler *profiler, unsigned long sample ) {
103
+	unsigned int sample_msb;
104
+	unsigned int mean_shift;
105
+	unsigned int delta_shift;
106
+	signed long pre_delta;
107
+	signed long post_delta;
108
+	signed long long accvar_delta;
109
+	unsigned int accvar_delta_shift;
110
+	unsigned int accvar_delta_msb;
111
+	unsigned int accvar_shift;
112
+
113
+	/* Our scaling logic assumes that sample values never overflow
114
+	 * a signed long (i.e. that the high bit is always zero).
115
+	 */
116
+	assert ( ( ( signed ) sample ) >= 0 );
117
+
118
+	/* Update sample count */
119
+	profiler->count++;
120
+
121
+	/* Adjust mean sample value scale if necessary.  Skip if
122
+	 * sample is zero (in which case flsl(sample)-1 would
123
+	 * underflow): in the case of a zero sample we have no need to
124
+	 * adjust the scale anyway.
125
+	 */
126
+	if ( sample ) {
127
+		sample_msb = ( flsl ( sample ) - 1 );
128
+		if ( profiler->mean_msb < sample_msb ) {
129
+			profiler->mean >>= ( sample_msb - profiler->mean_msb );
130
+			profiler->mean_msb = sample_msb;
131
+		}
132
+	}
133
+
134
+	/* Scale sample to internal units */
135
+	mean_shift = profile_mean_shift ( profiler );
136
+	sample <<= mean_shift;
137
+
138
+	/* Update mean */
139
+	pre_delta = ( sample - profiler->mean );
140
+	profiler->mean += ( pre_delta / ( ( signed ) profiler->count ) );
141
+	post_delta = ( sample - profiler->mean );
142
+	delta_shift = mean_shift;
143
+	DBGC ( profiler, "PROFILER %p sample %#lx mean %s", profiler,
144
+	       ( sample >> mean_shift ),
145
+		profile_hex_fraction ( profiler->mean, mean_shift ) );
146
+	DBGC ( profiler, " pre %s",
147
+	       profile_hex_fraction ( pre_delta, delta_shift ) );
148
+	DBGC ( profiler, " post %s\n",
149
+	       profile_hex_fraction ( post_delta, delta_shift ) );
150
+
151
+	/* Scale both deltas to fit in half of an unsigned long long
152
+	 * to avoid potential overflow on multiplication.  Note that
153
+	 * shifting a signed quantity is "implementation-defined"
154
+	 * behaviour in the C standard, but gcc documents that it will
155
+	 * always perform sign extension.
156
+	 */
157
+	if ( sizeof ( pre_delta ) > ( sizeof ( accvar_delta ) / 2 ) ) {
158
+		unsigned int shift = ( 8 * ( sizeof ( pre_delta ) -
159
+					     ( sizeof ( accvar_delta ) / 2 ) ));
160
+		pre_delta >>= shift;
161
+		post_delta >>= shift;
162
+		delta_shift -= shift;
163
+	}
164
+
165
+	/* Update variance, if applicable.  Skip if either delta is
166
+	 * zero (in which case flsl(delta)-1 would underflow): in the
167
+	 * case of a zero delta there is no change to the accumulated
168
+	 * variance anyway.
169
+	 */
170
+	if ( pre_delta && post_delta ) {
171
+
172
+		/* Calculate variance delta */
173
+		accvar_delta = ( ( ( signed long long ) pre_delta ) *
174
+				 ( ( signed long long ) post_delta ) );
175
+		accvar_delta_shift = ( 2 * delta_shift );
176
+		assert ( accvar_delta > 0 );
177
+
178
+		/* Calculate variance delta MSB, using flsl() on each
179
+		 * delta individually to provide an upper bound rather
180
+		 * than requiring the existence of flsll().
181
+		 */
182
+		accvar_delta_msb = ( flsll ( accvar_delta ) - 1 );
183
+		if ( accvar_delta_msb > accvar_delta_shift ) {
184
+			accvar_delta_msb -= accvar_delta_shift;
185
+		} else {
186
+			accvar_delta_msb = 0;
187
+		}
188
+
189
+		/* Adjust scales as necessary */
190
+		if ( profiler->accvar_msb < accvar_delta_msb ) {
191
+			/* Rescale accumulated variance */
192
+			profiler->accvar >>= ( accvar_delta_msb -
193
+					       profiler->accvar_msb );
194
+			profiler->accvar_msb = accvar_delta_msb;
195
+		} else {
196
+			/* Rescale variance delta */
197
+			accvar_delta >>= ( profiler->accvar_msb -
198
+					   accvar_delta_msb );
199
+			accvar_delta_shift -= ( profiler->accvar_msb -
200
+						accvar_delta_msb );
201
+		}
202
+
203
+		/* Scale delta to internal units */
204
+		accvar_shift = profile_accvar_shift ( profiler );
205
+		accvar_delta <<= ( accvar_shift - accvar_delta_shift );
206
+
207
+		/* Accumulate variance */
208
+		profiler->accvar += accvar_delta;
209
+
210
+		/* Adjust scale if necessary */
211
+		if ( profiler->accvar &
212
+		     ( 1ULL << ( ( 8 * sizeof ( profiler->accvar ) ) - 1 ) ) ) {
213
+			profiler->accvar >>= 1;
214
+			profiler->accvar_msb++;
215
+			accvar_delta >>= 1;
216
+			accvar_shift--;
217
+		}
218
+
219
+		DBGC ( profiler, "PROFILER %p accvar %s", profiler,
220
+		       profile_hex_fraction ( profiler->accvar, accvar_shift ));
221
+		DBGC ( profiler, " delta %s\n",
222
+		       profile_hex_fraction ( accvar_delta, accvar_shift ) );
223
+	}
224
+}
225
+
226
+/**
227
+ * Get mean sample value
228
+ *
229
+ * @v profiler		Profiler
230
+ * @ret mean		Mean sample value
231
+ */
232
+unsigned long profile_mean ( struct profiler *profiler ) {
233
+	unsigned int mean_shift = profile_mean_shift ( profiler );
234
+
235
+	/* Round to nearest and scale down to original units */
236
+	return ( ( profiler->mean + ( 1UL << ( mean_shift - 1 ) ) )
237
+		 >> mean_shift );
238
+}
239
+
240
+/**
241
+ * Get sample variance
242
+ *
243
+ * @v profiler		Profiler
244
+ * @ret variance	Sample variance
245
+ */
246
+unsigned long profile_variance ( struct profiler *profiler ) {
247
+	unsigned int accvar_shift = profile_accvar_shift ( profiler );
248
+
249
+	/* Variance is zero if fewer than two samples exist (avoiding
250
+	 * division by zero error).
251
+	 */
252
+	if ( profiler->count < 2 )
253
+		return 0;
254
+
255
+	/* Calculate variance, round to nearest, and scale to original units */
256
+	return ( ( ( profiler->accvar / ( profiler->count - 1 ) )
257
+		   + ( 1ULL << ( accvar_shift - 1 ) ) ) >> accvar_shift );
258
+}
259
+
260
+/**
261
+ * Get sample standard deviation
262
+ *
263
+ * @v profiler		Profiler
264
+ * @ret stddev		Sample standard deviation
265
+ */
266
+unsigned long profile_stddev ( struct profiler *profiler ) {
267
+
268
+	return isqrt ( profile_variance ( profiler ) );
269
+}

+ 61
- 47
src/include/ipxe/profile.h Переглянути файл

@@ -10,71 +10,85 @@
10 10
 FILE_LICENCE ( GPL2_OR_LATER );
11 11
 
12 12
 #include <stdint.h>
13
+#include <bits/profile.h>
14
+#include <ipxe/tables.h>
15
+
16
+#ifdef NDEBUG
17
+#define PROFILING 0
18
+#else
19
+#define PROFILING 1
20
+#endif
13 21
 
14 22
 /**
15 23
  * A data structure for storing profiling information
16 24
  */
17
-union profiler {
18
-	/** Timestamp (in CPU-specific "ticks") */
19
-	uint64_t timestamp;
20
-	/** Registers returned by rdtsc.
25
+struct profiler {
26
+	/** Name */
27
+	const char *name;
28
+	/** Start timestamp */
29
+	uint64_t started;
30
+	/** Number of samples */
31
+	unsigned int count;
32
+	/** Mean sample value (scaled) */
33
+	unsigned long mean;
34
+	/** Mean sample value MSB
35
+	 *
36
+	 * This is the highest bit set in the raw (unscaled) value
37
+	 * (i.e. one less than would be returned by flsl(raw_mean)).
38
+	 */
39
+	unsigned int mean_msb;
40
+	/** Accumulated variance (scaled) */
41
+	unsigned long long accvar;
42
+	/** Accumulated variance MSB
21 43
 	 *
22
-	 * This part should really be architecture-specific code.
44
+	 * This is the highest bit set in the raw (unscaled) value
45
+	 * (i.e. one less than would be returned by flsll(raw_accvar)).
23 46
 	 */
24
-	struct {
25
-		uint32_t eax;
26
-		uint32_t edx;
27
-	} rdtsc;
47
+	unsigned int accvar_msb;
28 48
 };
29 49
 
30
-/**
31
- * Static per-object profiler, for use with simple_profile()
32
- */
33
-static union profiler simple_profiler;
50
+/** Profiler table */
51
+#define PROFILERS __table ( struct profiler, "profilers" )
52
+
53
+/** Declare a profiler */
54
+#if PROFILING
55
+#define __profiler __table_entry ( PROFILERS, 01 )
56
+#else
57
+#define __profiler
58
+#endif
59
+
60
+extern void profile_update ( struct profiler *profiler, unsigned long sample );
61
+extern unsigned long profile_mean ( struct profiler *profiler );
62
+extern unsigned long profile_variance ( struct profiler *profiler );
63
+extern unsigned long profile_stddev ( struct profiler *profiler );
34 64
 
35 65
 /**
36
- * Perform profiling
66
+ * Start profiling
37 67
  *
38
- * @v profiler		Profiler data structure
39
- * @ret delta		Elapsed ticks since last call to profile().
40
- *
41
- * Call profile() both before and after the code you wish to measure.
42
- * The "after" call will return the measurement.  For example:
43
- *
44
- * @code
45
- *
46
- *     profile ( &profiler );
47
- *     ... do something here ...
48
- *     printf ( "It took %ld ticks to execute\n", profile ( &profiler ) );
49
- *
50
- * @endcode
68
+ * @v profiler		Profiler
51 69
  */
52
-static inline __attribute__ (( always_inline )) unsigned long
53
-profile ( union profiler *profiler ) {
54
-	uint64_t last_timestamp = profiler->timestamp;
70
+static inline __attribute__ (( always_inline )) void
71
+profile_start ( struct profiler *profiler ) {
55 72
 
56
-	__asm__ __volatile__ ( "rdtsc" :
57
-			       "=a" ( profiler->rdtsc.eax ),
58
-			       "=d" ( profiler->rdtsc.edx ) );
59
-	return ( profiler->timestamp - last_timestamp );
73
+	/* If profiling is active then record start timestamp */
74
+	if ( PROFILING )
75
+		profiler->started = profile_timestamp();
60 76
 }
61 77
 
62 78
 /**
63
- * Perform profiling
79
+ * Record profiling result
64 80
  *
65
- * @ret delta		Elapsed ticks since last call to profile().
66
- *
67
- * When you only need one profiler, you can avoid the hassle of
68
- * creating your own @c profiler data structure by using
69
- * simple_profile() instead.
70
- *
71
- * simple_profile() is equivalent to profile(&simple_profiler), where
72
- * @c simple_profiler is a @c profiler data structure that is static
73
- * to each object which includes @c profile.h.
81
+ * @v profiler		Profiler
74 82
  */
75
-static inline __attribute__ (( always_inline )) unsigned long
76
-simple_profile ( void ) {
77
-	return profile ( &simple_profiler );
83
+static inline __attribute__ (( always_inline )) void
84
+profile_stop ( struct profiler *profiler ) {
85
+	uint64_t ended;
86
+
87
+	/* If profiling is active then record end timestamp and update stats */
88
+	if ( PROFILING ) {
89
+		ended = profile_timestamp();
90
+		profile_update ( profiler, ( ended - profiler->started ) );
91
+	}
78 92
 }
79 93
 
80 94
 #endif /* _IPXE_PROFILE_H */

+ 13
- 7
src/tests/cbc_test.c Переглянути файл

@@ -36,6 +36,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
36 36
 #include <ipxe/profile.h>
37 37
 #include "cbc_test.h"
38 38
 
39
+/** Number of sample iterations for profiling */
40
+#define PROFILE_COUNT 16
41
+
39 42
 /**
40 43
  * Test CBC encryption
41 44
  *
@@ -115,8 +118,7 @@ static unsigned long cbc_cost ( struct cipher_algorithm *cipher,
115 118
 	uint8_t key[key_len];
116 119
 	uint8_t iv[cipher->blocksize];
117 120
 	uint8_t ctx[cipher->ctxsize];
118
-	union profiler profiler;
119
-	unsigned long long elapsed;
121
+	struct profiler profiler;
120 122
 	unsigned long cost;
121 123
 	unsigned int i;
122 124
 	int rc;
@@ -135,13 +137,17 @@ static unsigned long cbc_cost ( struct cipher_algorithm *cipher,
135 137
 	assert ( rc == 0 );
136 138
 	cipher_setiv ( cipher, ctx, iv );
137 139
 
138
-	/* Time operation */
139
-	profile ( &profiler );
140
-	op ( cipher, ctx, random, random, sizeof ( random ) );
141
-	elapsed = profile ( &profiler );
140
+	/* Profile cipher operation */
141
+	memset ( &profiler, 0, sizeof ( profiler ) );
142
+	for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
143
+		profile_start ( &profiler );
144
+		op ( cipher, ctx, random, random, sizeof ( random ) );
145
+		profile_stop ( &profiler );
146
+	}
142 147
 
143 148
 	/* Round to nearest whole number of cycles per byte */
144
-	cost = ( ( elapsed + ( sizeof ( random ) / 2 ) ) / sizeof ( random ) );
149
+	cost = ( ( profile_mean ( &profiler ) + ( sizeof ( random ) / 2 ) ) /
150
+		 sizeof ( random ) );
145 151
 
146 152
 	return cost;
147 153
 }

+ 18
- 9
src/tests/digest_test.c Переглянути файл

@@ -25,12 +25,18 @@ FILE_LICENCE ( GPL2_OR_LATER );
25 25
  *
26 26
  */
27 27
 
28
+/* Forcibly enable assertions */
29
+#undef NDEBUG
30
+
28 31
 #include <stdlib.h>
29 32
 #include <string.h>
30 33
 #include <ipxe/crypto.h>
31 34
 #include <ipxe/profile.h>
32 35
 #include "digest_test.h"
33 36
 
37
+/** Number of sample iterations for profiling */
38
+#define PROFILE_COUNT 16
39
+
34 40
 /**
35 41
  * Test digest algorithm
36 42
  *
@@ -81,8 +87,7 @@ unsigned long digest_cost ( struct digest_algorithm *digest ) {
81 87
 	static uint8_t random[8192]; /* Too large for stack */
82 88
 	uint8_t ctx[digest->ctxsize];
83 89
 	uint8_t out[digest->digestsize];
84
-	union profiler profiler;
85
-	unsigned long long elapsed;
90
+	struct profiler profiler;
86 91
 	unsigned long cost;
87 92
 	unsigned int i;
88 93
 
@@ -91,15 +96,19 @@ unsigned long digest_cost ( struct digest_algorithm *digest ) {
91 96
 	for ( i = 0 ; i < sizeof ( random ) ; i++ )
92 97
 		random[i] = rand();
93 98
 
94
-	/* Time digest calculation */
95
-	profile ( &profiler );
96
-	digest_init ( digest, ctx );
97
-	digest_update ( digest, ctx, random, sizeof ( random ) );
98
-	digest_final ( digest, ctx, out );
99
-	elapsed = profile ( &profiler );
99
+	/* Profile digest calculation */
100
+	memset ( &profiler, 0, sizeof ( profiler ) );
101
+	for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
102
+		profile_start ( &profiler );
103
+		digest_init ( digest, ctx );
104
+		digest_update ( digest, ctx, random, sizeof ( random ) );
105
+		digest_final ( digest, ctx, out );
106
+		profile_stop ( &profiler );
107
+	}
100 108
 
101 109
 	/* Round to nearest whole number of cycles per byte */
102
-	cost = ( ( elapsed + ( sizeof ( random ) / 2 ) ) / sizeof ( random ) );
110
+	cost = ( ( profile_mean ( &profiler ) + ( sizeof ( random ) / 2 ) ) /
111
+		 sizeof ( random ) );
103 112
 
104 113
 	return cost;
105 114
 }

+ 16
- 8
src/tests/memcpy_test.c Переглянути файл

@@ -34,6 +34,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
34 34
 #include <ipxe/test.h>
35 35
 #include <ipxe/profile.h>
36 36
 
37
+/** Number of sample iterations for profiling */
38
+#define PROFILE_COUNT 16
39
+
37 40
 /* Provide global functions to allow inspection of generated code */
38 41
 
39 42
 void memcpy_0 ( void *dest, void *src ) { memcpy ( dest, src, 0 ); }
@@ -120,10 +123,10 @@ __attribute__ (( noinline )) void * memcpy_var ( void *dest, const void *src,
120 123
  */
121 124
 static void memcpy_test_speed ( unsigned int dest_offset,
122 125
 				unsigned int src_offset, size_t len ) {
126
+	struct profiler profiler;
123 127
 	uint8_t *dest;
124 128
 	uint8_t *src;
125 129
 	unsigned int i;
126
-	unsigned long elapsed;
127 130
 
128 131
 	/* Allocate blocks */
129 132
 	dest = malloc ( len + dest_offset );
@@ -135,21 +138,26 @@ static void memcpy_test_speed ( unsigned int dest_offset,
135 138
 	for ( i = 0 ; i < len ; i++ )
136 139
 		src[ src_offset + i ] = random();
137 140
 
138
-	/* Perform memcpy() */
139
-	simple_profile();
141
+	/* Check correctness of copied data */
140 142
 	memcpy ( ( dest + dest_offset ), ( src + src_offset ), len );
141
-	elapsed = simple_profile();
142
-
143
-	/* Check copied data */
144 143
 	ok ( memcmp ( ( dest + dest_offset ), ( src + src_offset ),
145 144
 		      len ) == 0 );
146 145
 
146
+	/* Profile memcpy() */
147
+	memset ( &profiler, 0, sizeof ( profiler ) );
148
+	for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
149
+		profile_start ( &profiler );
150
+		memcpy ( ( dest + dest_offset ), ( src + src_offset ), len );
151
+		profile_stop ( &profiler );
152
+	}
153
+
147 154
 	/* Free blocks */
148 155
 	free ( dest );
149 156
 	free ( src );
150 157
 
151
-	DBG ( "MEMCPY copied %zd bytes (+%d => +%d) in %ld ticks\n",
152
-	      len, src_offset, dest_offset, elapsed );
158
+	DBG ( "MEMCPY copied %zd bytes (+%d => +%d) in %ld +/- %ld ticks\n",
159
+	      len, src_offset, dest_offset, profile_mean ( &profiler ),
160
+	      profile_stddev ( &profiler ) );
153 161
 }
154 162
 
155 163
 /**

+ 140
- 0
src/tests/profile_test.c Переглянути файл

@@ -0,0 +1,140 @@
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
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+/** @file
23
+ *
24
+ * Profiling self-tests
25
+ *
26
+ */
27
+
28
+/* Forcibly enable assertions */
29
+#undef NDEBUG
30
+
31
+#include <string.h>
32
+#include <assert.h>
33
+#include <ipxe/test.h>
34
+#include <ipxe/profile.h>
35
+
36
+/** A profiling test */
37
+struct profile_test {
38
+	/** Sample values */
39
+	const unsigned long *samples;
40
+	/** Number of samples */
41
+	unsigned int count;
42
+	/** Expected mean sample value */
43
+	unsigned long mean;
44
+	/** Expected standard deviation */
45
+	unsigned long stddev;
46
+};
47
+
48
+/** Define inline data */
49
+#define DATA(...) { __VA_ARGS__ }
50
+
51
+/** Define a profiling test */
52
+#define PROFILE_TEST( name, MEAN, STDDEV, SAMPLES )			\
53
+	static const unsigned long name ## _samples[] = SAMPLES;	\
54
+	static struct profile_test name = {				\
55
+		.samples = name ## _samples,				\
56
+		.count = ( sizeof ( name ## _samples ) /		\
57
+			   sizeof ( name ## _samples [0] ) ),		\
58
+		.mean = MEAN,						\
59
+		.stddev = STDDEV,					\
60
+	}
61
+
62
+/** Empty data set */
63
+PROFILE_TEST ( empty, 0, 0, DATA() );
64
+
65
+/** Single-element data set (zero) */
66
+PROFILE_TEST ( zero, 0, 0, DATA ( 0 ) );
67
+
68
+/** Single-element data set (non-zero) */
69
+PROFILE_TEST ( single, 42, 0, DATA ( 42 ) );
70
+
71
+/** Multiple identical element data set */
72
+PROFILE_TEST ( identical, 69, 0, DATA ( 69, 69, 69, 69, 69, 69, 69 ) );
73
+
74
+/** Small element data set */
75
+PROFILE_TEST ( small, 5, 2, DATA ( 3, 5, 9, 4, 3, 2, 5, 7 ) );
76
+
77
+/** Random data set */
78
+PROFILE_TEST ( random, 70198, 394,
79
+	       DATA ( 69772, 70068, 70769, 69653, 70663, 71078, 70101, 70341,
80
+		      70215, 69600, 70020, 70456, 70421, 69972, 70267, 69999,
81
+		      69972 ) );
82
+
83
+/** Large-valued random data set */
84
+PROFILE_TEST ( large, 93533894UL, 25538UL,
85
+	       DATA ( 93510333UL, 93561169UL, 93492361UL, 93528647UL,
86
+		      93557566UL, 93503465UL, 93540126UL, 93549020UL,
87
+		      93502307UL, 93527320UL, 93537152UL, 93540125UL,
88
+		      93550773UL, 93586731UL, 93521312UL ) );
89
+
90
+/**
91
+ * Report a profiling test result
92
+ *
93
+ * @v test		Profiling test
94
+ * @v file		Test code file
95
+ * @v line		Test code line
96
+ */
97
+static void profile_okx ( struct profile_test *test, const char *file,
98
+			  unsigned int line ) {
99
+	struct profiler profiler;
100
+	unsigned long mean;
101
+	unsigned long stddev;
102
+	unsigned int i;
103
+
104
+	/* Initialise profiler */
105
+	memset ( &profiler, 0, sizeof ( profiler ) );
106
+
107
+	/* Record sample values */
108
+	for ( i = 0 ; i < test->count ; i++ )
109
+		profile_update ( &profiler, test->samples[i] );
110
+
111
+	/* Check resulting statistics */
112
+	mean = profile_mean ( &profiler );
113
+	stddev = profile_stddev ( &profiler );
114
+	DBGC ( test, "PROFILE calculated mean %ld stddev %ld\n", mean, stddev );
115
+	okx ( mean == test->mean, file, line );
116
+	okx ( stddev == test->stddev, file, line );
117
+}
118
+#define profile_ok( test ) profile_okx ( test, __FILE__, __LINE__ )
119
+
120
+/**
121
+ * Perform profiling self-tests
122
+ *
123
+ */
124
+static void profile_test_exec ( void ) {
125
+
126
+	/* Perform profiling tests */
127
+	profile_ok ( &empty );
128
+	profile_ok ( &zero );
129
+	profile_ok ( &single );
130
+	profile_ok ( &identical );
131
+	profile_ok ( &small );
132
+	profile_ok ( &random );
133
+	profile_ok ( &large );
134
+}
135
+
136
+/** Profiling self-test */
137
+struct self_test profile_test __self_test = {
138
+	.name = "profile",
139
+	.exec = profile_test_exec,
140
+};

+ 16
- 5
src/tests/tcpip_test.c Переглянути файл

@@ -36,6 +36,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
36 36
 #include <ipxe/profile.h>
37 37
 #include <ipxe/tcpip.h>
38 38
 
39
+/** Number of sample iterations for profiling */
40
+#define PROFILE_COUNT 16
41
+
39 42
 /** A TCP/IP fixed-data test */
40 43
 struct tcpip_test {
41 44
 	/** Data */
@@ -173,10 +176,10 @@ static void tcpip_okx ( struct tcpip_test *test, const char *file,
173 176
 static void tcpip_random_okx ( struct tcpip_random_test *test,
174 177
 			       const char *file, unsigned int line ) {
175 178
 	uint8_t *data = ( tcpip_data + test->offset );
179
+	struct profiler profiler;
176 180
 	uint16_t expected;
177 181
 	uint16_t generic_sum;
178 182
 	uint16_t sum;
179
-	unsigned long elapsed;
180 183
 	unsigned int i;
181 184
 
182 185
 	/* Sanity check */
@@ -194,12 +197,20 @@ static void tcpip_random_okx ( struct tcpip_random_test *test,
194 197
 	okx ( generic_sum == expected, file, line );
195 198
 
196 199
 	/* Verify optimised tcpip_continue_chksum() result */
197
-	simple_profile();
198 200
 	sum = tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data, test->len );
199
-	elapsed = simple_profile();
200 201
 	okx ( sum == expected, file, line );
201
-	DBG ( "TCPIP checksummed %zd bytes (+%zd) in %ld ticks\n",
202
-	      test->len, test->offset, elapsed );
202
+
203
+	/* Profile optimised calculation */
204
+	memset ( &profiler, 0, sizeof ( profiler ) );
205
+	for ( i = 0 ; i < PROFILE_COUNT ; i++ ) {
206
+		profile_start ( &profiler );
207
+		sum = tcpip_continue_chksum ( TCPIP_EMPTY_CSUM, data,
208
+					      test->len );
209
+		profile_stop ( &profiler );
210
+	}
211
+	DBG ( "TCPIP checksummed %zd bytes (+%zd) in %ld +/- %ld ticks\n",
212
+	      test->len, test->offset, profile_mean ( &profiler ),
213
+	      profile_stddev ( &profiler ) );
203 214
 }
204 215
 #define tcpip_random_ok( test ) tcpip_random_okx ( test, __FILE__, __LINE__ )
205 216
 

+ 1
- 0
src/tests/tests.c Переглянути файл

@@ -55,3 +55,4 @@ REQUIRE_OBJECT ( deflate_test );
55 55
 REQUIRE_OBJECT ( png_test );
56 56
 REQUIRE_OBJECT ( dns_test );
57 57
 REQUIRE_OBJECT ( uri_test );
58
+REQUIRE_OBJECT ( profile_test );

Завантаження…
Відмінити
Зберегти