Skip to content

Commit e5cd2d4

Browse files
committed
source/pkeyread.c: add verbose output option
Add -v option, that provides additional information about the test run, including additional statistics, such as minimum, maximum, median per-thread times and standard deviation. Signed-off-by: Eugene Syromiatnikov <[email protected]> Reviewed-by: Neil Horman <[email protected]> (Merged from #37)
1 parent 6b8cfb5 commit e5cd2d4

File tree

2 files changed

+132
-36
lines changed

2 files changed

+132
-36
lines changed

source/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ regen_key_samples:
4949
./genkeys_setpeer.sh > keys_setpeer.h
5050

5151
pkeyread: pkeyread.c keys.h libperf.a
52-
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o pkeyread pkeyread.c -lperf -lcrypto
52+
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o pkeyread pkeyread.c -lperf -lcrypto -lm
5353

5454
evp_setpeer: evp_setpeer.c libperf.a
5555
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o evp_setpeer evp_setpeer.c -lperf -lcrypto

source/pkeyread.c

Lines changed: 131 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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>
@@ -27,6 +28,23 @@
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

3149
size_t num_calls;
3250
size_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

190270
static 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[])
216299
int 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

Comments
 (0)