77 * https://www.openssl.org/source/license.html
88 */
99
10+ #include <math.h> /* sqrt() */
1011#include <stdlib.h>
1112#include <stdio.h>
1213#include <string.h>
2728#include "keys.h"
2829
2930#define RUN_TIME 5
31+ static size_t timeout_us = RUN_TIME * 1000000 ;
32+
33+ enum {
34+ VERBOSITY_TERSE ,
35+ VERBOSITY_DEFAULT ,
36+ VERBOSITY_VERBOSE ,
37+ };
38+
39+ struct call_times {
40+ double avg ;
41+ double min ;
42+ double max ;
43+ double stddev ;
44+ double median ;
45+ size_t min_idx ;
46+ size_t max_idx ;
47+ };
3048
3149size_t num_calls ;
3250size_t * counts ;
@@ -44,8 +62,10 @@ static void do_pemread(size_t num)
4462 size_t keydata_sz ;
4563 EVP_PKEY * key ;
4664 BIO * pem ;
47- size_t i ;
4865 OSSL_TIME time ;
66+ size_t count = 0 ;
67+
68+ counts [num ] = 0 ;
4969
5070 if (sample_id >= SAMPLE_ALL ) {
5171 fprintf (stderr , "%s no sample key set for test\n" , __func__ );
@@ -64,29 +84,32 @@ static void do_pemread(size_t num)
6484 return ;
6585 }
6686
67- counts [num ] = 0 ;
68-
6987 /*
7088 * Technically this includes the EVP_PKEY_free() in the timing - but I
7189 * think we can live with that
7290 */
7391 do {
7492 key = PEM_read_bio_PrivateKey (pem , NULL , NULL , NULL );
7593 if (key == NULL ) {
76- fprintf (stderr , "Failed to create key: %llu [%s PEM]\n" ,
77- (unsigned long long )i ,
94+ fprintf (stderr , "Failed to create key [%s PEM]\n" ,
7895 sample_names [sample_id ]);
7996 err = 1 ;
80- BIO_free (pem );
81- return ;
97+ goto end ;
8298 }
8399 EVP_PKEY_free (key );
84- BIO_reset (pem );
100+ if (BIO_reset (pem ) == 0 ) {
101+ fprintf (stderr , "Failed to reset BIO [%s PEM]\n" ,
102+ sample_names [sample_id ]);
103+ err = 1 ;
104+ goto end ;
105+ }
85106
86- counts [ num ] ++ ;
107+ count ++ ;
87108 time = ossl_time_now ();
88109 } while (time .t < max_time .t );
89110
111+ end :
112+ counts [num ] = count ;
90113 BIO_free (pem );
91114}
92115
@@ -158,41 +181,101 @@ static int format_name_to_id(const char *format_name)
158181 return i ;
159182}
160183
161- static double get_avcalltime (void )
184+ static int cmp_size_t (const void * a_ptr , const void * b_ptr )
185+ {
186+ const size_t * const a = a_ptr ;
187+ const size_t * const b = b_ptr ;
188+
189+ return * a - * b ;
190+ }
191+
192+ static void get_calltimes (struct call_times * times , int verbosity )
162193{
163194 int i ;
164195 size_t total_count = 0 ;
165- double avcalltime ;
196+ size_t min_count = SIZE_MAX ;
197+ size_t max_count = 0 ;
166198
167- for (i = 0 ; i < threadcount ; i ++ )
199+ for (i = 0 ; i < threadcount ; i ++ ) {
168200 total_count += counts [i ];
169201
170- avcalltime = (double )RUN_TIME * 1e6 * threadcount / total_count ;
202+ if (verbosity >= VERBOSITY_VERBOSE ) {
203+ if (counts [i ] < min_count ) {
204+ min_count = counts [i ];
205+ times -> max_idx = i ;
206+ }
207+
208+ if (counts [i ] > max_count ) {
209+ max_count = counts [i ];
210+ times -> min_idx = i ;
211+ }
212+ }
213+ }
214+
215+ times -> avg = (double ) timeout_us * threadcount / total_count ;
171216
172- return avcalltime ;
217+ if (verbosity >= VERBOSITY_VERBOSE ) {
218+ double variance = 0 ;
219+
220+ /* Maximum count means minimum time and vice versa */
221+ times -> min = (double ) timeout_us / max_count ;
222+ times -> max = (double ) timeout_us / min_count ;
223+
224+ qsort (counts , threadcount , sizeof (counts [0 ]), cmp_size_t );
225+ times -> median = (double ) timeout_us / counts [threadcount / 2 ];
226+
227+ for (i = 0 ; i < threadcount ; i ++ ) {
228+ double dev = (double ) timeout_us / counts [i ] - times -> avg ;
229+
230+ variance += dev * dev ;
231+ }
232+
233+ times -> stddev = sqrt (variance / threadcount );
234+ }
173235}
174236
175- static void report_result (int key_id , int format_id , int terse )
237+ static void report_result (int key_id , int format_id , int verbosity )
176238{
239+ struct call_times times = { 0 };
240+
177241 if (err ) {
178- fprintf (stderr , "Error during test of %s in %s format\n" ,
179- sample_names [key_id ], format_names [format_id ]);
180- exit (EXIT_FAILURE );
242+ fprintf (stderr , "Error during test of %s in %s format\n" ,
243+ sample_names [key_id ], format_names [format_id ]);
244+ exit (EXIT_FAILURE );
181245 }
182246
183- if (terse )
184- printf ("%lf\n" , get_avcalltime ());
185- else
186- printf ("Average time per %s(%s) call: %lfus\n" ,
187- format_names [format_id ], sample_names [key_id ], get_avcalltime ());
247+ get_calltimes (& times , verbosity );
248+
249+ switch (verbosity ) {
250+ case VERBOSITY_TERSE :
251+ printf ("%lf\n" , times .avg );
252+ break ;
253+ case VERBOSITY_DEFAULT :
254+ printf ("Average time per %s(%s) call: %lfus\n" ,
255+ format_names [format_id ], sample_names [key_id ], times .avg );
256+ break ;
257+ case VERBOSITY_VERBOSE :
258+ printf ("%s(%s):%*s avg: %9.3lf us, median: %9.3lf us"
259+ ", min: %9.3lf us @thread %3zu, max: %9.3lf us @thread %3zu"
260+ ", stddev: %9.3lf us (%8.4lf%%)\n" ,
261+ format_names [format_id ], sample_names [key_id ],
262+ (int ) (10 - strlen (format_names [format_id ]) - strlen (sample_names [key_id ])), "" ,
263+ times .avg , times .median ,
264+ times .min , times .min_idx , times .max , times .max_idx ,
265+ times .stddev , 100.0 * times .stddev / times .avg );
266+ break ;
267+ }
188268}
189269
190270static void usage (char * const argv [])
191271{
192272 const char * * key_name = sample_names ;
193273 const char * * format_name = format_names ;
194274
195- fprintf (stderr , "%s -k key_name -f format_name [-t] terse threadcount\n"
275+ fprintf (stderr , "%s -k key_name -f format_name [-t] [-v] [-T time] threadcount\n"
276+ "\t-t terse output\n"
277+ "\t-v verbose output, includes min, max, stddev, and median times\n"
278+ "\t-T timeout for each test run in seconds, can be fractional"
196279 "\twhere key_name is one of these: " , argv [0 ]);
197280 fprintf (stderr , "%s" , * key_name );
198281 do {
@@ -216,35 +299,48 @@ static void usage(char * const argv[])
216299int main (int argc , char * const argv [])
217300{
218301 OSSL_TIME duration ;
219- int ch , i ;
302+ int ch ;
220303 int key_id , key_id_min , key_id_max , k ;
221304 int format_id , format_id_min , format_id_max , f ;
222- int terse = 0 ;
305+ int verbosity = VERBOSITY_DEFAULT ;
223306 char * key = NULL ;
224307 char * key_format = NULL ;
225- int kf ;
226308 void (* do_f [2 ])(size_t ) = {
227309 do_pemread ,
228310 do_derread
229311 };
230- const char * fname [] = {
231- "PEM_read_bio_PrivateKey" ,
232- "X509_PUBKEY_get0_param"
233- };
234312
235313 key_id = SAMPLE_INVALID ;
236314 format_id = FORMAT_INVALID ;
237315
238- while ((ch = getopt (argc , argv , "k:f:t " )) != -1 ) {
316+ while ((ch = getopt (argc , argv , "T: k:f:tv " )) != -1 ) {
239317 switch (ch ) {
318+ case 'T' : {
319+ double timeout_s ;
320+ char * endptr ;
321+
322+ timeout_s = strtod (optarg , & endptr );
323+
324+ if (endptr == NULL || * endptr != '\0' ) {
325+ fprintf (stderr , "incorrect timeout value: \"%s\"\n" , optarg );
326+ usage (argv );
327+ return EXIT_FAILURE ;
328+ }
329+
330+ timeout_us = timeout_s * 1e6 ;
331+ break ;
332+ }
240333 case 'k' :
241334 key = optarg ;
242335 break ;
243336 case 'f' :
244337 key_format = optarg ;
245338 break ;
246339 case 't' :
247- terse = 1 ;
340+ verbosity = VERBOSITY_TERSE ;
341+ break ;
342+ case 'v' :
343+ verbosity = VERBOSITY_VERBOSE ;
248344 break ;
249345 }
250346 }
@@ -297,7 +393,6 @@ int main(int argc, char * const argv[])
297393 usage (argv );
298394 return EXIT_FAILURE ;
299395 }
300- max_time = ossl_time_add (ossl_time_now (), ossl_seconds2time (RUN_TIME ));
301396
302397 counts = OPENSSL_malloc (sizeof (size_t ) * threadcount );
303398 if (counts == NULL ) {
@@ -323,13 +418,14 @@ int main(int argc, char * const argv[])
323418 for (k = key_id_min ; k < key_id_max ; k ++ ) {
324419 for (f = format_id_min ; f < format_id_max ; f ++ ) {
325420 sample_id = k ;
421+ max_time = ossl_time_add (ossl_time_now (), ossl_us2time (timeout_us ));
326422 if (!perflib_run_multi_thread_test (do_f [f ], threadcount , & duration )) {
327423 fprintf (stderr , "Failed to run the test %s in %s format]\n" ,
328424 sample_names [k ], format_names [f ]);
329425 OPENSSL_free (counts );
330426 return EXIT_FAILURE ;
331427 }
332- report_result (k , f , terse );
428+ report_result (k , f , verbosity );
333429 }
334430 }
335431
0 commit comments