Add a performance test for PEM_read_bio_PrivateKey()
authorMatt Caswell <matt@openssl.org>
Tue, 11 Jul 2023 15:21:26 +0000 (16:21 +0100)
committerMatt Caswell <matt@openssl.org>
Fri, 14 Jul 2023 09:29:17 +0000 (10:29 +0100)
We repeatedly attempt to load an RSA Private Key and measure performance.

Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/tools/pull/161)

perf/Makefile
perf/pemread.c [new file with mode: 0644]
perf/perflib/perfhelper.c
perf/perflib/perflib.h

index 1570f49108f7557f40f146ca37f545d2a8c654f9..39bee484048bc79c85020ed856f44f6b4cd9fcbd 100644 (file)
@@ -1,7 +1,7 @@
-all: randbytes handshake sslnew newrawkey rsasign x509storeissuer providerdoall
+all: randbytes handshake sslnew newrawkey rsasign x509storeissuer providerdoall pemread
 
 clean:
-       rm libperf.a *.o randbytes handshake sslnew newrawkey rsasign x509storeissuer providerdoall
+       rm libperf.a *.o randbytes handshake sslnew newrawkey rsasign x509storeissuer providerdoall pemread
 
 libperf.a: perflib/*.c perflib/*.h
        gcc -I$(TARGET_OSSL_INCLUDE_PATH) -I. -c perflib/*.c
@@ -27,3 +27,6 @@ x509storeissuer: x509storeissuer.c libperf.a
 
 providerdoall: providerdoall.c libperf.a
        gcc -L$(TARGET_OSSL_LIBRARY_PATH) -L. -I$(TARGET_OSSL_INCLUDE_PATH) -I. -o providerdoall providerdoall.c -lperf -lcrypto
+
+pemread:       pemread.c libperf.a
+       gcc -L$(TARGET_OSSL_LIBRARY_PATH) -L. -I$(TARGET_OSSL_INCLUDE_PATH) -I. -o pemread pemread.c -lperf -lcrypto
diff --git a/perf/pemread.c b/perf/pemread.c
new file mode 100644 (file)
index 0000000..06deb7d
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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/pem.h>
+#include <openssl/evp.h>
+#include <openssl/crypto.h>
+#include "perflib/perflib.h"
+
+#define NUM_CALLS_PER_BLOCK         100
+#define NUM_CALL_BLOCKS_PER_THREAD  100
+#define NUM_CALLS_PER_THREAD        (NUM_CALLS_PER_BLOCK * NUM_CALL_BLOCKS_PER_THREAD)
+
+int err = 0;
+
+const char *pemdataraw[] = {
+    "-----BEGIN RSA PRIVATE KEY-----\n",
+    "MIIBOgIBAAJBAMFcGsaxxdgiuuGmCkVImy4h99CqT7jwY3pexPGcnUFtR2Fh36Bp\n",
+    "oncwtkZ4cAgtvd4Qs8PkxUdp6p/DlUmObdkCAwEAAQJAUR44xX6zB3eaeyvTRzms\n",
+    "kHADrPCmPWnr8dxsNwiDGHzrMKLN+i/HAam+97HxIKVWNDH2ba9Mf1SA8xu9dcHZ\n",
+    "AQIhAOHPCLxbtQFVxlnhSyxYeb7O323c3QulPNn3bhOipElpAiEA2zZpBE8ZXVnL\n",
+    "74QjG4zINlDfH+EOEtjJJ3RtaYDugvECIBtsQDxXytChsRgDQ1TcXdStXPcDppie\n",
+    "dZhm8yhRTTBZAiAZjE/U9rsIDC0ebxIAZfn3iplWh84yGB3pgUI3J5WkoQIhAInE\n",
+    "HTUY5WRj5riZtkyGnbm3DvF+1eMtO2lYV+OuLcfE\n",
+    "-----END RSA PRIVATE KEY-----\n",
+    NULL
+};
+
+void do_pemread(size_t num)
+{
+    EVP_PKEY *key;
+    BIO *pem;
+    int i;
+    char *pemdata;
+    size_t len;
+
+    pemdata = perflib_glue_strings(pemdataraw, &len);
+    if (pemdata == NULL) {
+        printf("Cannot create PEM data\n");
+        err = 1;
+        return;
+    }
+
+    pem = BIO_new_mem_buf(pemdata, len);
+    if (pem == NULL) {
+        printf("Cannot create mem BIO\n");
+        err = 1;
+        return;
+    }
+
+    /*
+     * Technically this includes the EVP_PKEY_free() in the timing - but I
+     * think we can live with that
+     */
+    for (i = 0; i < NUM_CALLS_PER_THREAD; i++) {
+        key = PEM_read_bio_PrivateKey(pem, NULL, NULL, NULL);
+        if (key == NULL) {
+            printf("Failed to create key: %d\n", i);
+            err = 1;
+            BIO_free(pem);
+            return;
+        }
+        EVP_PKEY_free(key);
+        BIO_reset(pem);
+    }
+
+    BIO_free(pem);
+}
+
+int main(int argc, char *argv[])
+{
+    int threadcount;
+    OSSL_TIME duration;
+    uint64_t us;
+    double avcalltime;
+    int terse = 0;
+    int argnext;
+
+    if ((argc != 2 && argc != 3)
+                || (argc == 3 && strcmp("--terse", argv[1]) != 0)) {
+        printf("Usage: pemread [--terse] threadcount\n");
+        return EXIT_FAILURE;
+    }
+
+    if (argc == 3) {
+        terse = 1;
+        argnext = 2;
+    } else {
+        argnext = 1;
+    }
+
+    threadcount = atoi(argv[argnext]);
+    if (threadcount < 1) {
+        printf("threadcount must be > 0\n");
+        return EXIT_FAILURE;
+    }
+
+    if (!perflib_run_multi_thread_test(do_pemread, threadcount, &duration)) {
+        printf("Failed to run the test\n");
+        return EXIT_FAILURE;
+    }
+
+    if (err) {
+        printf("Error during test\n");
+        return EXIT_FAILURE;
+    }
+
+    us = ossl_time2us(duration);
+
+    avcalltime = (double)us / (NUM_CALL_BLOCKS_PER_THREAD * threadcount);
+
+    if (terse)
+        printf("%lf\n", avcalltime);
+    else
+        printf("Average time per %d PEM_read_bio_PrivateKey() calls: %lfus\n",
+               NUM_CALLS_PER_BLOCK, avcalltime);
+
+    return EXIT_SUCCESS;
+}
index e3e3284b525c1970e3d617f89b59b960768ba8b2..1cbad5929bd73c2e39cb4348ac9f62061224c244 100644 (file)
@@ -31,3 +31,29 @@ char *perflib_mk_file_path(const char *dir, const char *file)
 
     return full_file;
 }
+
+/*
+ * Glue an array of strings together and return it as an allocated string.
+ * Optionally return the whole length of this string in |out_len|
+ */
+char *perflib_glue_strings(const char *list[], size_t *out_len)
+{
+    size_t len = 0;
+    char *p, *ret;
+    int i;
+
+    for (i = 0; list[i] != NULL; i++)
+        len += strlen(list[i]);
+
+    if (out_len != NULL)
+        *out_len = len;
+
+    ret = p = OPENSSL_malloc(len + 1);
+    if (p == NULL)
+        return NULL;
+
+    for (i = 0; list[i] != NULL; i++)
+        p += strlen(strcpy(p, list[i]));
+
+    return ret;
+}
index d0570ab73d7d2ee7ad831d922999e8824f6e1ed8..14b2fd31dcbc0280d72fc9c1855b8de3bedaf5a7 100644 (file)
@@ -33,6 +33,7 @@ typedef pthread_t thread_t;
 int perflib_run_multi_thread_test(void (*f)(size_t), size_t threadcount,
                                   OSSL_TIME *duration);
 char *perflib_mk_file_path(const char *dir, const char *file);
+char *perflib_glue_strings(const char *list[], size_t *out_len);
 
 int perflib_create_ssl_ctx_pair(const SSL_METHOD *sm, const SSL_METHOD *cm,
                                 int min_proto_version, int max_proto_version,