-
-
Notifications
You must be signed in to change notification settings - Fork 52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
adding a fetch test to perf tools #193
Changes from 3 commits
ce6d3ee
8ea71da
69e0b7e
1308515
ea28a2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,272 @@ | ||
/* | ||
* Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. | ||
* | ||
* Licensed under the Apache License 2.0 (the "License"). You may not use | ||
* this file except in compliance with the License. You can obtain a copy | ||
* in the file LICENSE in the source distribution or at | ||
* https://www.openssl.org/source/license.html | ||
*/ | ||
|
||
#include <stdlib.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <openssl/evp.h> | ||
#include <openssl/kdf.h> | ||
#include <openssl/core_names.h> | ||
#include "perflib/perflib.h" | ||
|
||
#define NUM_CALLS_PER_TEST 100000 | ||
|
||
OSSL_TIME *times; | ||
|
||
int err = 0; | ||
|
||
static int threadcount; | ||
|
||
static OSSL_LIB_CTX *ctx = NULL; | ||
|
||
#define ARRAY_SIZE(a) \ | ||
((sizeof(a) / sizeof(*(a)))) | ||
|
||
typedef enum { | ||
FETCH_MD = 0, | ||
FETCH_CIPHER, | ||
FETCH_KDF, | ||
FETCH_MAC, | ||
FETCH_RAND, | ||
FETCH_END | ||
} fetch_type_t; | ||
|
||
struct fetch_type_map { | ||
char *name; | ||
fetch_type_t id; | ||
}; | ||
|
||
struct fetch_type_map type_map[] = { | ||
{ "MD" , FETCH_MD }, | ||
{ "CIPHER", FETCH_CIPHER }, | ||
{ "KDF" , FETCH_KDF }, | ||
{ "MAC" , FETCH_MAC }, | ||
{ "RAND" , FETCH_RAND } | ||
}; | ||
|
||
fetch_type_t exclusive_fetch_type = FETCH_END; | ||
char *exclusive_fetch_alg = NULL; | ||
|
||
struct fetch_data_entry { | ||
fetch_type_t ftype; | ||
const char *alg; | ||
const char *propq; | ||
}; | ||
|
||
static struct fetch_data_entry fetch_entries[] = { | ||
nhorman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{FETCH_MD, OSSL_DIGEST_NAME_SHA2_224, NULL}, | ||
{FETCH_MD, OSSL_DIGEST_NAME_SHA2_256, NULL}, | ||
{FETCH_MD, OSSL_DIGEST_NAME_SHA3_224, NULL}, | ||
{FETCH_MD, OSSL_DIGEST_NAME_SHA3_256, NULL}, | ||
#ifndef OPENSSL_NO_SIV | ||
{FETCH_CIPHER, OSSL_CIPHER_NAME_AES_128_GCM_SIV, NULL}, | ||
{FETCH_CIPHER, OSSL_CIPHER_NAME_AES_192_GCM_SIV, NULL}, | ||
{FETCH_CIPHER, OSSL_CIPHER_NAME_AES_256_GCM_SIV, NULL}, | ||
#endif | ||
{FETCH_KDF, OSSL_KDF_NAME_HKDF, NULL}, | ||
#ifndef OPENSSL_NO_SCRYPT | ||
{FETCH_KDF, OSSL_KDF_NAME_SCRYPT, NULL}, | ||
#endif | ||
{FETCH_KDF, OSSL_KDF_NAME_KRB5KDF, NULL}, | ||
{FETCH_KDF, OSSL_KDF_NAME_KBKDF, NULL}, | ||
#ifndef OPENSSL_NO_BLAKE2 | ||
{FETCH_MAC, OSSL_MAC_NAME_BLAKE2BMAC, NULL}, | ||
#endif | ||
#ifndef OPENSSL_NO_CMAC | ||
{FETCH_MAC, OSSL_MAC_NAME_CMAC, NULL}, | ||
#endif | ||
{FETCH_MAC, OSSL_MAC_NAME_GMAC, NULL}, | ||
{FETCH_MAC, OSSL_MAC_NAME_HMAC, NULL}, | ||
#ifndef OPENSSL_NO_POLY1305 | ||
{FETCH_MAC, OSSL_MAC_NAME_POLY1305, NULL}, | ||
#endif | ||
}; | ||
|
||
void do_fetch(size_t num) | ||
{ | ||
OSSL_TIME start, end; | ||
size_t i, j; | ||
const char *fetch_alg = NULL; | ||
|
||
start = ossl_time_now(); | ||
|
||
for (i = 0; i < NUM_CALLS_PER_TEST / threadcount; i++) { | ||
nhorman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/* | ||
* If we set a fetch type, always use that | ||
*/ | ||
if (exclusive_fetch_type == FETCH_END) { | ||
j = i % ARRAY_SIZE(fetch_entries); | ||
fetch_alg = fetch_entries[j].alg; | ||
} else { | ||
j = exclusive_fetch_type; | ||
fetch_alg = exclusive_fetch_alg; | ||
} | ||
|
||
if (err == 1) | ||
return; | ||
|
||
switch (fetch_entries[j].ftype) { | ||
case FETCH_MD: | ||
EVP_MD *md = EVP_MD_fetch(ctx, fetch_alg, | ||
fetch_entries[j].propq); | ||
if (md == NULL) { | ||
fprintf(stderr, "Failed to fetch %s\n", fetch_alg); | ||
err = 1; | ||
return; | ||
} | ||
EVP_MD_free(md); | ||
break; | ||
case FETCH_CIPHER: | ||
EVP_CIPHER *cph = EVP_CIPHER_fetch(ctx, fetch_alg, | ||
fetch_entries[j].propq); | ||
if (cph == NULL) { | ||
fprintf(stderr, "Failed to fetch %s\n", fetch_alg); | ||
err = 1; | ||
return; | ||
} | ||
EVP_CIPHER_free(cph); | ||
break; | ||
case FETCH_KDF: | ||
EVP_KDF *kdf = EVP_KDF_fetch(ctx, fetch_alg, | ||
fetch_entries[j].propq); | ||
if (kdf == NULL) { | ||
fprintf(stderr, "Failed to fetch %s\n", fetch_alg); | ||
err = 1; | ||
return; | ||
} | ||
EVP_KDF_free(kdf); | ||
break; | ||
case FETCH_MAC: | ||
EVP_MAC *mac = EVP_MAC_fetch(ctx, fetch_alg, | ||
fetch_entries[j].propq); | ||
if (mac == NULL) { | ||
fprintf(stderr, "Failed to fetch %s\n", fetch_alg); | ||
err = 1; | ||
return; | ||
} | ||
EVP_MAC_free(mac); | ||
break; | ||
case FETCH_RAND: | ||
EVP_RAND *rnd = EVP_RAND_fetch(ctx, fetch_alg, | ||
fetch_entries[j].propq); | ||
if (rnd == NULL) { | ||
fprintf(stderr, "Failed to fetch %s\n", fetch_alg); | ||
err = 1; | ||
return; | ||
} | ||
EVP_RAND_free(rnd); | ||
break; | ||
default: | ||
err = 1; | ||
return; | ||
} | ||
} | ||
end = ossl_time_now(); | ||
times[num] = ossl_time_subtract(end, start); | ||
} | ||
|
||
int main(int argc, char *argv[]) | ||
{ | ||
OSSL_TIME duration; | ||
OSSL_TIME ttime; | ||
double real_num_calls; | ||
double av; | ||
int terse = 0; | ||
int argnext; | ||
size_t i; | ||
int rc = EXIT_FAILURE; | ||
char *fetch_type = getenv("EVP_FETCH_TYPE"); | ||
paulidale marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if ((argc != 2 && argc != 3) | ||
|| (argc == 3 && strcmp("--terse", argv[1]) != 0)) { | ||
printf("Usage: %s [--terse] threadcount\n", argv[0]); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
if (argc == 3) { | ||
terse = 1; | ||
argnext = 2; | ||
} else { | ||
argnext = 1; | ||
} | ||
|
||
if (fetch_type != NULL) { | ||
exclusive_fetch_alg = strstr(fetch_type, ":"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if (exclusive_fetch_alg == NULL) { | ||
printf("Malformed EVP_FETCH_TYPE TYPE:ALG\n"); | ||
return EXIT_FAILURE; | ||
} | ||
/* Split the string into a type and alg */ | ||
*exclusive_fetch_alg = '\0'; | ||
exclusive_fetch_alg++; | ||
for (i = 0; i < ARRAY_SIZE(type_map); i++) { | ||
if (!strcmp(fetch_type, type_map[i].name)) { | ||
exclusive_fetch_type = type_map[i].id; | ||
break; | ||
} | ||
} | ||
if (i == ARRAY_SIZE(type_map)) { | ||
printf("EVP_FETCH_TYPE is invalid\n"); | ||
return EXIT_FAILURE; | ||
} | ||
} | ||
|
||
threadcount = atoi(argv[argnext]); | ||
if (threadcount < 1) { | ||
printf("threadcount must be > 0\n"); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
ctx = OSSL_LIB_CTX_new(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should have a "warm up" option that would do one fetch here outside the measurements. The initial fetch on a libctx with no providers loaded will load the default provider implicitly and fighting for that could skew the numbers fairly visibly. I.e. it would be interesting to see the differences in the numbers. Or instead we could explicitly load the default provider with OSSL_PROVIDER_load(). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose that would depend on the case we want to measure. I could envision wanting to measure both the fast path (where a fetch would always hit in the cache), and the slow path (where a fetch would never hit in the cache) Can we save that for a subsequent PR? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another PR I think. IMO a warm up isn't going to impact the measurement significantly given a large number of iterations per thread. |
||
if (ctx == NULL) | ||
return EXIT_FAILURE; | ||
|
||
times = OPENSSL_malloc(sizeof(OSSL_TIME) * threadcount); | ||
if (times == NULL) { | ||
printf("Failed to create times array\n"); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
if (!perflib_run_multi_thread_test(do_fetch, threadcount, &duration)) { | ||
printf("Failed to run the test\n"); | ||
goto out; | ||
} | ||
|
||
if (err) { | ||
printf("Error during test\n"); | ||
goto out; | ||
} | ||
|
||
ttime = times[0]; | ||
for (i = 1; i < threadcount; i++) | ||
ttime = ossl_time_add(ttime, times[i]); | ||
|
||
/* | ||
* EVP_PKEY_new_raw_public_key is pretty fast, running in | ||
nhorman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* only a few us. But ossl_time2us does integer division | ||
* and so because the average us computed above is less than | ||
* the value of OSSL_TIME_US, we wind up with truncation to | ||
* zero in the math. Instead, manually do the division, casting | ||
* our values as doubles so that we compute the proper time | ||
*/ | ||
real_num_calls = ((double)NUM_CALLS_PER_TEST / threadcount) * threadcount; | ||
av = ((double)ossl_time2ticks(ttime) / real_num_calls) /(double)OSSL_TIME_US; | ||
|
||
if (terse) | ||
printf("%lf\n", av); | ||
else | ||
printf("Average time per fetch call: %lfus\n", | ||
av); | ||
nhorman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
rc = EXIT_SUCCESS; | ||
out: | ||
OSSL_LIB_CTX_free(ctx); | ||
OPENSSL_free(times); | ||
return rc; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please consider raising this number. Even 10000000 calls takes just a few seconds on my laptop.