Use thread_local EVP_MD_CTX in isc_iterated_hash()

As this code is on hot path (NSEC3) this introduces an additional
optimization of the EVP_MD API - instead of calling EVP_MD_CTX_new() on
every call to isc_iterated_hash(), we create two thread_local objects
for each thread - a basectx and mdctx, initialize basectx once and then
use EVP_MD_CTX_copy_ex() to flip the initialized state into mdctx.  This
saves us couple more valuable microseconds from the isc_iterated_hash()
call.
This commit is contained in:
Ondřej Surý 2023-01-16 11:12:06 +01:00 committed by Ondřej Surý
parent 25db8d0103
commit f3753d591f
4 changed files with 71 additions and 13 deletions

View file

@ -35,4 +35,13 @@ isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
const int saltlength, const unsigned char *in,
const int inlength);
/*
* Private
*/
void
isc__iterated_hash_initialize(void);
void
isc__iterated_hash_shutdown(void);
ISC_LANG_ENDDECLS

View file

@ -65,26 +65,39 @@ isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
return (SHA_DIGEST_LENGTH);
}
#else
void
isc__iterated_hash_initialize(void) {
/* empty */
}
void
isc__iterated_hash_shutdown(void) {
/* empty */
}
#else /* HAVE_SHA1_INIT */
#include <openssl/evp.h>
#include <isc/md.h>
static thread_local bool initialized = false;
static thread_local EVP_MD_CTX *mdctx = NULL;
static thread_local EVP_MD_CTX *basectx = NULL;
int
isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
const int iterations, const unsigned char *salt,
const int saltlength, const unsigned char *in,
const int inlength) {
REQUIRE(out != NULL);
REQUIRE(mdctx != NULL);
REQUIRE(basectx != NULL);
int n = 0;
size_t len;
unsigned int outlength = 0;
const unsigned char *buf;
EVP_MD_CTX *ctx = EVP_MD_CTX_create();
RUNTIME_CHECK(ctx != NULL);
if (hashalg != 1) {
return (0);
@ -92,21 +105,20 @@ isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
buf = in;
len = inlength;
do {
if (EVP_DigestInit_ex(ctx, ISC_MD_SHA1, NULL) != 1) {
if (EVP_MD_CTX_copy_ex(mdctx, basectx) != 1) {
goto fail;
}
if (EVP_DigestUpdate(ctx, buf, len) != 1) {
if (EVP_DigestUpdate(mdctx, buf, len) != 1) {
goto fail;
}
if (EVP_DigestUpdate(ctx, salt, saltlength) != 1) {
if (EVP_DigestUpdate(mdctx, salt, saltlength) != 1) {
goto fail;
}
if (EVP_DigestFinal_ex(ctx, out, &outlength) != 1) {
if (EVP_DigestFinal_ex(mdctx, out, &outlength) != 1) {
goto fail;
}
@ -114,13 +126,41 @@ isc_iterated_hash(unsigned char *out, const unsigned int hashalg,
len = outlength;
} while (n++ < iterations);
EVP_MD_CTX_free(ctx);
return (outlength);
fail:
EVP_MD_CTX_free(ctx);
return (0);
}
#endif
void
isc__iterated_hash_initialize(void) {
if (initialized) {
return;
}
basectx = EVP_MD_CTX_new();
INSIST(basectx != NULL);
mdctx = EVP_MD_CTX_new();
INSIST(mdctx != NULL);
RUNTIME_CHECK(EVP_DigestInit_ex(basectx, ISC_MD_SHA1, NULL) == 1);
initialized = true;
}
void
isc__iterated_hash_shutdown(void) {
if (!initialized) {
return;
}
REQUIRE(mdctx != NULL);
EVP_MD_CTX_free(mdctx);
mdctx = NULL;
REQUIRE(basectx != NULL);
EVP_MD_CTX_free(basectx);
basectx = NULL;
initialized = false;
}
#endif /* HAVE_SHA1_INIT */

View file

@ -14,6 +14,7 @@
/*! \file */
#include <isc/bind9.h>
#include <isc/iterated_hash.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/os.h>
@ -51,11 +52,13 @@ isc__initialize(void) {
isc__uv_initialize();
isc__xml_initialize();
isc__md_initialize();
isc__iterated_hash_initialize();
(void)isc_os_ncpus();
}
void
isc__shutdown(void) {
isc__iterated_hash_shutdown();
isc__md_shutdown();
isc__xml_shutdown();
isc__uv_shutdown();

View file

@ -16,6 +16,8 @@
#include <inttypes.h>
#include <stdlib.h>
#include <isc/iterated_hash.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/once.h>
#include <isc/thread.h>
@ -194,9 +196,13 @@ isc__trampoline_run(isc_threadarg_t arg) {
isc__trampoline_attach(trampoline);
isc__iterated_hash_initialize();
/* Run the main function */
result = (trampoline->start)(trampoline->arg);
isc__iterated_hash_shutdown();
isc__trampoline_detach(trampoline);
return (result);