2017-03-16 13:00:23 +08:00
|
|
|
/*
|
2024-09-05 15:35:49 +08:00
|
|
|
* Copyright 2017-2024 The OpenSSL Project Authors. All Rights Reserved.
|
2017-06-15 11:34:28 +08:00
|
|
|
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
2017-03-16 13:00:23 +08:00
|
|
|
*
|
2018-12-06 20:05:25 +08:00
|
|
|
* Licensed under the Apache License 2.0 (the "License"). You may not use
|
2017-03-16 13:00:23 +08:00
|
|
|
* 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 <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <openssl/opensslconf.h>
|
|
|
|
#include <openssl/lhash.h>
|
|
|
|
#include <openssl/err.h>
|
2024-03-02 05:28:53 +08:00
|
|
|
#include <openssl/rand.h>
|
2017-03-16 13:00:23 +08:00
|
|
|
#include <openssl/crypto.h>
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
#include <internal/hashtable.h>
|
2017-03-16 13:00:23 +08:00
|
|
|
|
2017-08-22 20:35:43 +08:00
|
|
|
#include "internal/nelem.h"
|
2024-03-02 05:28:53 +08:00
|
|
|
#include "threadstest.h"
|
2017-03-16 13:00:23 +08:00
|
|
|
#include "testutil.h"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The macros below generate unused functions which error out one of the clang
|
|
|
|
* builds. We disable this check here.
|
|
|
|
*/
|
|
|
|
#ifdef __clang__
|
|
|
|
#pragma clang diagnostic ignored "-Wunused-function"
|
|
|
|
#endif
|
|
|
|
|
2022-03-22 19:52:27 +08:00
|
|
|
DEFINE_LHASH_OF_EX(int);
|
2017-03-16 13:00:23 +08:00
|
|
|
|
|
|
|
static int int_tests[] = { 65537, 13, 1, 3, -5, 6, 7, 4, -10, -12, -14, 22, 9,
|
|
|
|
-17, 16, 17, -23, 35, 37, 173, 11 };
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
static const size_t n_int_tests = OSSL_NELEM(int_tests);
|
2017-03-16 13:00:23 +08:00
|
|
|
static short int_found[OSSL_NELEM(int_tests)];
|
2020-10-09 07:36:50 +08:00
|
|
|
static short int_not_found;
|
2017-03-16 13:00:23 +08:00
|
|
|
|
|
|
|
static unsigned long int int_hash(const int *p)
|
|
|
|
{
|
|
|
|
return 3 & *p; /* To force collisions */
|
|
|
|
}
|
|
|
|
|
|
|
|
static int int_cmp(const int *p, const int *q)
|
|
|
|
{
|
|
|
|
return *p != *q;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int int_find(int n)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = 0; i < n_int_tests; i++)
|
|
|
|
if (int_tests[i] == n)
|
|
|
|
return i;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void int_doall(int *v)
|
|
|
|
{
|
2020-10-09 07:36:50 +08:00
|
|
|
const int n = int_find(*v);
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
int_not_found++;
|
|
|
|
else
|
|
|
|
int_found[n]++;
|
2017-03-16 13:00:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void int_doall_arg(int *p, short *f)
|
|
|
|
{
|
2020-10-09 07:36:50 +08:00
|
|
|
const int n = int_find(*p);
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
int_not_found++;
|
|
|
|
else
|
|
|
|
f[n]++;
|
2017-03-16 13:00:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
IMPLEMENT_LHASH_DOALL_ARG(int, short);
|
|
|
|
|
|
|
|
static int test_int_lhash(void)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
int data;
|
|
|
|
int null;
|
|
|
|
} dels[] = {
|
|
|
|
{ 65537, 0 },
|
|
|
|
{ 173, 0 },
|
|
|
|
{ 999, 1 },
|
|
|
|
{ 37, 0 },
|
|
|
|
{ 1, 0 },
|
2017-06-06 23:35:43 +08:00
|
|
|
{ 34, 1 }
|
2017-03-16 13:00:23 +08:00
|
|
|
};
|
|
|
|
const unsigned int n_dels = OSSL_NELEM(dels);
|
|
|
|
LHASH_OF(int) *h = lh_int_new(&int_hash, &int_cmp);
|
|
|
|
unsigned int i;
|
|
|
|
int testresult = 0, j, *p;
|
|
|
|
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr(h))
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* insert */
|
|
|
|
for (i = 0; i < n_int_tests; i++)
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr_null(lh_int_insert(h, int_tests + i))) {
|
|
|
|
TEST_info("int insert %d", i);
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* num_items */
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
if (!TEST_int_eq((size_t)lh_int_num_items(h), n_int_tests))
|
2017-03-22 12:27:55 +08:00
|
|
|
goto end;
|
2017-03-16 13:00:23 +08:00
|
|
|
|
|
|
|
/* retrieve */
|
|
|
|
for (i = 0; i < n_int_tests; i++)
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_int_eq(*lh_int_retrieve(h, int_tests + i), int_tests[i])) {
|
|
|
|
TEST_info("lhash int retrieve value %d", i);
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
for (i = 0; i < n_int_tests; i++)
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr_eq(lh_int_retrieve(h, int_tests + i), int_tests + i)) {
|
|
|
|
TEST_info("lhash int retrieve address %d", i);
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
j = 1;
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr_eq(lh_int_retrieve(h, &j), int_tests + 2))
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* replace */
|
|
|
|
j = 13;
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr(p = lh_int_insert(h, &j)))
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr_eq(p, int_tests + 1))
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr_eq(lh_int_retrieve(h, int_tests + 1), &j))
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* do_all */
|
|
|
|
memset(int_found, 0, sizeof(int_found));
|
2020-10-09 07:36:50 +08:00
|
|
|
int_not_found = 0;
|
2017-03-16 13:00:23 +08:00
|
|
|
lh_int_doall(h, &int_doall);
|
2020-10-09 07:36:50 +08:00
|
|
|
if (!TEST_int_eq(int_not_found, 0)) {
|
|
|
|
TEST_info("lhash int doall encountered a not found condition");
|
|
|
|
goto end;
|
|
|
|
}
|
2017-03-16 13:00:23 +08:00
|
|
|
for (i = 0; i < n_int_tests; i++)
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_int_eq(int_found[i], 1)) {
|
|
|
|
TEST_info("lhash int doall %d", i);
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
}
|
2017-06-06 23:35:43 +08:00
|
|
|
|
2017-03-16 13:00:23 +08:00
|
|
|
/* do_all_arg */
|
|
|
|
memset(int_found, 0, sizeof(int_found));
|
2020-10-09 07:36:50 +08:00
|
|
|
int_not_found = 0;
|
2017-03-16 13:00:23 +08:00
|
|
|
lh_int_doall_short(h, int_doall_arg, int_found);
|
2020-10-09 07:36:50 +08:00
|
|
|
if (!TEST_int_eq(int_not_found, 0)) {
|
|
|
|
TEST_info("lhash int doall arg encountered a not found condition");
|
|
|
|
goto end;
|
|
|
|
}
|
2017-03-16 13:00:23 +08:00
|
|
|
for (i = 0; i < n_int_tests; i++)
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_int_eq(int_found[i], 1)) {
|
|
|
|
TEST_info("lhash int doall arg %d", i);
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
}
|
2017-06-06 23:35:43 +08:00
|
|
|
|
2017-03-16 13:00:23 +08:00
|
|
|
/* delete */
|
|
|
|
for (i = 0; i < n_dels; i++) {
|
|
|
|
const int b = lh_int_delete(h, &dels[i].data) == NULL;
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_int_eq(b ^ dels[i].null, 0)) {
|
|
|
|
TEST_info("lhash int delete %d", i);
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* error */
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_int_eq(lh_int_error(h), 0))
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
|
|
|
|
testresult = 1;
|
|
|
|
end:
|
|
|
|
lh_int_free(h);
|
|
|
|
return testresult;
|
|
|
|
}
|
|
|
|
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
|
|
|
|
static int int_filter_all(HT_VALUE *v, void *arg)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
HT_START_KEY_DEFN(intkey)
|
|
|
|
HT_DEF_KEY_FIELD(mykey, int)
|
|
|
|
HT_END_KEY_DEFN(INTKEY)
|
|
|
|
|
|
|
|
IMPLEMENT_HT_VALUE_TYPE_FNS(int, test, static)
|
|
|
|
|
|
|
|
static int int_foreach(HT_VALUE *v, void *arg)
|
|
|
|
{
|
|
|
|
int *vd = ossl_ht_test_int_from_value(v);
|
|
|
|
const int n = int_find(*vd);
|
|
|
|
|
|
|
|
if (n < 0)
|
|
|
|
int_not_found++;
|
|
|
|
else
|
|
|
|
int_found[n]++;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint64_t hashtable_hash(uint8_t *key, size_t keylen)
|
|
|
|
{
|
|
|
|
return (uint64_t)(*(uint32_t *)key);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int test_int_hashtable(void)
|
|
|
|
{
|
|
|
|
static struct {
|
|
|
|
int data;
|
|
|
|
int should_del;
|
|
|
|
} dels[] = {
|
|
|
|
{ 65537 , 1},
|
|
|
|
{ 173 , 1},
|
|
|
|
{ 999 , 0 },
|
|
|
|
{ 37 , 1 },
|
|
|
|
{ 1 , 1 },
|
|
|
|
{ 34 , 0 }
|
|
|
|
};
|
|
|
|
const size_t n_dels = OSSL_NELEM(dels);
|
|
|
|
HT_CONFIG hash_conf = {
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
0,
|
2024-05-27 22:49:15 +08:00
|
|
|
1,
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
};
|
|
|
|
INTKEY key;
|
|
|
|
int rc = 0;
|
|
|
|
size_t i;
|
|
|
|
HT *ht = NULL;
|
|
|
|
int todel;
|
|
|
|
HT_VALUE_LIST *list = NULL;
|
|
|
|
|
|
|
|
ht = ossl_ht_new(&hash_conf);
|
|
|
|
|
|
|
|
if (ht == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* insert */
|
|
|
|
HT_INIT_KEY(&key);
|
|
|
|
for (i = 0; i < n_int_tests; i++) {
|
|
|
|
HT_SET_KEY_FIELD(&key, mykey, int_tests[i]);
|
|
|
|
if (!TEST_int_eq(ossl_ht_test_int_insert(ht, TO_HT_KEY(&key),
|
|
|
|
&int_tests[i], NULL), 1)) {
|
|
|
|
TEST_info("int insert %zu", i);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* num_items */
|
|
|
|
if (!TEST_int_eq((size_t)ossl_ht_count(ht), n_int_tests))
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* foreach, no arg */
|
|
|
|
memset(int_found, 0, sizeof(int_found));
|
|
|
|
int_not_found = 0;
|
|
|
|
ossl_ht_foreach_until(ht, int_foreach, NULL);
|
|
|
|
if (!TEST_int_eq(int_not_found, 0)) {
|
|
|
|
TEST_info("hashtable int foreach encountered a not found condition");
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < n_int_tests; i++)
|
|
|
|
if (!TEST_int_eq(int_found[i], 1)) {
|
|
|
|
TEST_info("hashtable int foreach %zu", i);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* filter */
|
|
|
|
list = ossl_ht_filter(ht, 64, int_filter_all, NULL);
|
|
|
|
if (!TEST_int_eq((size_t)list->list_len, n_int_tests))
|
|
|
|
goto end;
|
|
|
|
ossl_ht_value_list_free(list);
|
|
|
|
|
|
|
|
/* delete */
|
|
|
|
for (i = 0; i < n_dels; i++) {
|
|
|
|
HT_SET_KEY_FIELD(&key, mykey, dels[i].data);
|
|
|
|
todel = ossl_ht_delete(ht, TO_HT_KEY(&key));
|
|
|
|
if (dels[i].should_del) {
|
|
|
|
if (!TEST_int_eq(todel, 1)) {
|
2024-03-02 05:28:53 +08:00
|
|
|
TEST_info("hashtable couldn't find entry %d to delete\n",
|
|
|
|
dels[i].data);
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!TEST_int_eq(todel, 0)) {
|
|
|
|
TEST_info("%d found an entry that shouldn't be there\n", dels[i].data);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = 1;
|
|
|
|
end:
|
|
|
|
ossl_ht_free(ht);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2017-03-16 13:00:23 +08:00
|
|
|
static unsigned long int stress_hash(const int *p)
|
|
|
|
{
|
|
|
|
return *p;
|
|
|
|
}
|
|
|
|
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
static int
|
|
|
|
timeval_subtract (struct timeval *result, struct timeval *x, struct timeval *y)
|
|
|
|
{
|
|
|
|
/* Perform the carry for the later subtraction by updating y. */
|
|
|
|
if (x->tv_usec < y->tv_usec) {
|
|
|
|
int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
|
|
|
|
y->tv_usec -= 1000000 * nsec;
|
|
|
|
y->tv_sec += nsec;
|
|
|
|
}
|
|
|
|
if (x->tv_usec - y->tv_usec > 1000000) {
|
|
|
|
int nsec = (x->tv_usec - y->tv_usec) / 1000000;
|
|
|
|
y->tv_usec += 1000000 * nsec;
|
|
|
|
y->tv_sec -= nsec;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute the time remaining to wait.
|
|
|
|
* tv_usec is certainly positive.
|
|
|
|
*/
|
|
|
|
result->tv_sec = x->tv_sec - y->tv_sec;
|
|
|
|
result->tv_usec = x->tv_usec - y->tv_usec;
|
|
|
|
|
|
|
|
/* Return 1 if result is negative. */
|
|
|
|
return x->tv_sec < y->tv_sec;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-03-16 13:00:23 +08:00
|
|
|
static int test_stress(void)
|
|
|
|
{
|
|
|
|
LHASH_OF(int) *h = lh_int_new(&stress_hash, &int_cmp);
|
|
|
|
const unsigned int n = 2500000;
|
|
|
|
unsigned int i;
|
|
|
|
int testresult = 0, *p;
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
struct timeval start, end, delta;
|
|
|
|
#endif
|
2017-03-16 13:00:23 +08:00
|
|
|
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr(h))
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
#endif
|
2017-03-16 13:00:23 +08:00
|
|
|
/* insert */
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
p = OPENSSL_malloc(sizeof(i));
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr(p)) {
|
|
|
|
TEST_info("lhash stress out of memory %d", i);
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
*p = 3 * i + 1;
|
|
|
|
lh_int_insert(h, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* num_items */
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_int_eq(lh_int_num_items(h), n))
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* delete in a different order */
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
const int j = (7 * i + 4) % n * 3 + 1;
|
|
|
|
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_ptr(p = lh_int_delete(h, &j))) {
|
|
|
|
TEST_info("lhash stress delete %d\n", i);
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
}
|
2017-03-22 12:27:55 +08:00
|
|
|
if (!TEST_int_eq(*p, j)) {
|
|
|
|
TEST_info("lhash stress bad value %d", i);
|
2017-03-16 13:00:23 +08:00
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
OPENSSL_free(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
testresult = 1;
|
|
|
|
end:
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
timeval_subtract(&delta, &end, &start);
|
|
|
|
TEST_info("lhash stress runs in %ld.%ld seconds", delta.tv_sec, delta.tv_usec);
|
|
|
|
#endif
|
2017-03-16 13:00:23 +08:00
|
|
|
lh_int_free(h);
|
|
|
|
return testresult;
|
|
|
|
}
|
|
|
|
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
static void hashtable_intfree(HT_VALUE *v)
|
|
|
|
{
|
|
|
|
OPENSSL_free(v->value);
|
|
|
|
}
|
|
|
|
|
2024-08-16 21:40:43 +08:00
|
|
|
static int test_hashtable_stress(int idx)
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
{
|
|
|
|
const unsigned int n = 2500000;
|
|
|
|
unsigned int i;
|
|
|
|
int testresult = 0, *p;
|
|
|
|
HT_CONFIG hash_conf = {
|
|
|
|
NULL, /* use default context */
|
|
|
|
hashtable_intfree, /* our free function */
|
|
|
|
hashtable_hash, /* our hash function */
|
|
|
|
625000, /* preset hash size */
|
2024-05-27 22:49:15 +08:00
|
|
|
1, /* Check collisions */
|
2024-08-16 21:40:43 +08:00
|
|
|
0 /* Lockless reads */
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
};
|
|
|
|
HT *h;
|
|
|
|
INTKEY key;
|
2024-08-16 21:40:43 +08:00
|
|
|
HT_VALUE *v;
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
struct timeval start, end, delta;
|
|
|
|
#endif
|
|
|
|
|
2024-08-16 21:40:43 +08:00
|
|
|
hash_conf.lockless_reads = idx;
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
h = ossl_ht_new(&hash_conf);
|
|
|
|
|
|
|
|
|
|
|
|
if (!TEST_ptr(h))
|
|
|
|
goto end;
|
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
HT_INIT_KEY(&key);
|
|
|
|
|
|
|
|
/* insert */
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
p = OPENSSL_malloc(sizeof(i));
|
|
|
|
if (!TEST_ptr(p)) {
|
|
|
|
TEST_info("hashtable stress out of memory %d", i);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
*p = 3 * i + 1;
|
|
|
|
HT_SET_KEY_FIELD(&key, mykey, *p);
|
|
|
|
if (!TEST_int_eq(ossl_ht_test_int_insert(h, TO_HT_KEY(&key),
|
|
|
|
p, NULL), 1)) {
|
|
|
|
TEST_info("hashtable unable to insert element %d\n", *p);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure we stored everything */
|
|
|
|
if (!TEST_int_eq((size_t)ossl_ht_count(h), n))
|
|
|
|
goto end;
|
|
|
|
|
2024-08-16 21:40:43 +08:00
|
|
|
/* delete or get in a different order */
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
const int j = (7 * i + 4) % n * 3 + 1;
|
|
|
|
HT_SET_KEY_FIELD(&key, mykey, j);
|
2024-08-16 21:40:43 +08:00
|
|
|
|
|
|
|
switch (idx) {
|
|
|
|
case 0:
|
|
|
|
if (!TEST_int_eq((ossl_ht_delete(h, TO_HT_KEY(&key))), 1)) {
|
|
|
|
TEST_info("hashtable didn't delete key %d\n", j);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (!TEST_ptr(p = ossl_ht_test_int_get(h, TO_HT_KEY(&key), &v))
|
|
|
|
|| !TEST_int_eq(*p, j)) {
|
|
|
|
TEST_info("hashtable didn't get key %d\n", j);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
break;
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
testresult = 1;
|
|
|
|
end:
|
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
timeval_subtract(&delta, &end, &start);
|
|
|
|
TEST_info("hashtable stress runs in %ld.%ld seconds", delta.tv_sec, delta.tv_usec);
|
|
|
|
#endif
|
|
|
|
ossl_ht_free(h);
|
|
|
|
return testresult;
|
|
|
|
}
|
|
|
|
|
2024-03-02 05:28:53 +08:00
|
|
|
typedef struct test_mt_entry {
|
|
|
|
int in_table;
|
|
|
|
int pending_delete;
|
|
|
|
} TEST_MT_ENTRY;
|
|
|
|
|
|
|
|
static HT *m_ht = NULL;
|
|
|
|
#define TEST_MT_POOL_SZ 256
|
2024-10-11 23:25:49 +08:00
|
|
|
#define TEST_THREAD_ITERATIONS 1000000
|
|
|
|
#define NUM_WORKERS 16
|
2024-03-02 05:28:53 +08:00
|
|
|
|
|
|
|
static struct test_mt_entry test_mt_entries[TEST_MT_POOL_SZ];
|
2024-10-11 23:25:49 +08:00
|
|
|
static char *worker_exits[NUM_WORKERS];
|
2024-03-02 05:28:53 +08:00
|
|
|
|
|
|
|
HT_START_KEY_DEFN(mtkey)
|
2024-10-11 23:13:40 +08:00
|
|
|
HT_DEF_KEY_FIELD(index, uint32_t)
|
2024-03-02 05:28:53 +08:00
|
|
|
HT_END_KEY_DEFN(MTKEY)
|
|
|
|
|
|
|
|
IMPLEMENT_HT_VALUE_TYPE_FNS(TEST_MT_ENTRY, mt, static)
|
|
|
|
|
|
|
|
static int worker_num = 0;
|
|
|
|
static CRYPTO_RWLOCK *worker_lock;
|
2024-10-11 23:13:40 +08:00
|
|
|
static CRYPTO_RWLOCK *testrand_lock;
|
2024-03-02 05:28:53 +08:00
|
|
|
static int free_failure = 0;
|
|
|
|
static int shutting_down = 0;
|
|
|
|
static int global_iteration = 0;
|
|
|
|
|
|
|
|
static void hashtable_mt_free(HT_VALUE *v)
|
|
|
|
{
|
|
|
|
TEST_MT_ENTRY *m = ossl_ht_mt_TEST_MT_ENTRY_from_value(v);
|
|
|
|
int pending_delete;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
CRYPTO_atomic_load_int(&m->pending_delete, &pending_delete, worker_lock);
|
|
|
|
|
|
|
|
if (shutting_down == 1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (pending_delete == 0) {
|
|
|
|
TEST_info("Freeing element which was not scheduled for free");
|
|
|
|
free_failure = 1;
|
|
|
|
} else {
|
|
|
|
CRYPTO_atomic_add(&m->pending_delete, -1,
|
|
|
|
&ret, worker_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DO_LOOKUP 0
|
|
|
|
#define DO_INSERT 1
|
|
|
|
#define DO_REPLACE 2
|
|
|
|
#define DO_DELETE 3
|
2024-10-11 23:13:40 +08:00
|
|
|
#define NUM_BEHAVIORS (DO_DELETE + 1)
|
2024-03-02 05:28:53 +08:00
|
|
|
|
|
|
|
static void do_mt_hash_work(void)
|
|
|
|
{
|
|
|
|
MTKEY key;
|
2024-10-11 23:13:40 +08:00
|
|
|
uint32_t index;
|
2024-03-02 05:28:53 +08:00
|
|
|
int num;
|
|
|
|
TEST_MT_ENTRY *m;
|
|
|
|
TEST_MT_ENTRY *expected_m = NULL;
|
|
|
|
HT_VALUE *v = NULL;
|
|
|
|
TEST_MT_ENTRY **r = NULL;
|
|
|
|
int expected_rc;
|
|
|
|
int ret;
|
|
|
|
char behavior;
|
|
|
|
size_t iter = 0;
|
|
|
|
int giter;
|
|
|
|
|
|
|
|
CRYPTO_atomic_add(&worker_num, 1, &num, worker_lock);
|
|
|
|
num--; /* atomic_add is an add/fetch operation */
|
|
|
|
|
|
|
|
HT_INIT_KEY(&key);
|
|
|
|
|
|
|
|
for (iter = 0; iter < TEST_THREAD_ITERATIONS; iter++) {
|
2024-10-11 23:13:40 +08:00
|
|
|
if (!TEST_true(CRYPTO_THREAD_write_lock(testrand_lock)))
|
2024-03-02 05:28:53 +08:00
|
|
|
return;
|
2024-10-11 23:13:40 +08:00
|
|
|
index = test_random() % TEST_MT_POOL_SZ;
|
|
|
|
behavior = (char)(test_random() % NUM_BEHAVIORS);
|
|
|
|
CRYPTO_THREAD_unlock(testrand_lock);
|
2024-03-02 05:28:53 +08:00
|
|
|
|
|
|
|
expected_m = &test_mt_entries[index];
|
|
|
|
HT_KEY_RESET(&key);
|
|
|
|
HT_SET_KEY_FIELD(&key, index, index);
|
|
|
|
|
|
|
|
if (!CRYPTO_atomic_add(&global_iteration, 1, &giter, worker_lock)) {
|
|
|
|
worker_exits[num] = "Unable to increment global iterator";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch(behavior) {
|
|
|
|
case DO_LOOKUP:
|
|
|
|
ossl_ht_read_lock(m_ht);
|
|
|
|
m = ossl_ht_mt_TEST_MT_ENTRY_get(m_ht, TO_HT_KEY(&key), &v);
|
|
|
|
if (m != NULL && m != expected_m) {
|
|
|
|
worker_exits[num] = "Read unexpected value from hashtable";
|
|
|
|
TEST_info("Iteration %d Read unexpected value %p when %p expected",
|
|
|
|
giter, (void *)m, (void *)expected_m);
|
|
|
|
}
|
|
|
|
ossl_ht_read_unlock(m_ht);
|
|
|
|
if (worker_exits[num] != NULL)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
case DO_INSERT:
|
|
|
|
case DO_REPLACE:
|
|
|
|
ossl_ht_write_lock(m_ht);
|
|
|
|
if (behavior == DO_REPLACE) {
|
|
|
|
expected_rc = 1;
|
|
|
|
r = &m;
|
|
|
|
} else {
|
|
|
|
expected_rc = !expected_m->in_table;
|
|
|
|
r = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (expected_rc != ossl_ht_mt_TEST_MT_ENTRY_insert(m_ht,
|
|
|
|
TO_HT_KEY(&key),
|
|
|
|
expected_m, r)) {
|
2024-10-11 23:13:40 +08:00
|
|
|
TEST_info("Iteration %d Expected rc %d on %s of element %u which is %s\n",
|
2024-03-02 05:28:53 +08:00
|
|
|
giter, expected_rc, behavior == DO_REPLACE ? "replace" : "insert",
|
2024-10-11 23:13:40 +08:00
|
|
|
(unsigned int)index,
|
|
|
|
expected_m->in_table ? "in table" : "not in table");
|
2024-03-02 05:28:53 +08:00
|
|
|
worker_exits[num] = "Failure on insert";
|
|
|
|
}
|
|
|
|
if (expected_rc == 1)
|
|
|
|
expected_m->in_table = 1;
|
|
|
|
ossl_ht_write_unlock(m_ht);
|
|
|
|
if (worker_exits[num] != NULL)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
case DO_DELETE:
|
|
|
|
ossl_ht_write_lock(m_ht);
|
|
|
|
expected_rc = expected_m->in_table;
|
|
|
|
if (expected_rc != ossl_ht_delete(m_ht, TO_HT_KEY(&key))) {
|
2024-10-11 23:13:40 +08:00
|
|
|
TEST_info("Iteration %d Expected rc %d on delete of element %u which is %s\n",
|
|
|
|
giter, expected_rc, (unsigned int)index,
|
2024-03-02 05:28:53 +08:00
|
|
|
expected_m->in_table ? "in table" : "not in table");
|
|
|
|
worker_exits[num] = "Failure on delete";
|
|
|
|
}
|
|
|
|
if (expected_rc == 1) {
|
|
|
|
expected_m->in_table = 0;
|
|
|
|
CRYPTO_atomic_add(&expected_m->pending_delete, 1, &ret, worker_lock);
|
|
|
|
}
|
|
|
|
ossl_ht_write_unlock(m_ht);
|
|
|
|
if (worker_exits[num] != NULL)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
worker_exits[num] = "Undefined behavior specified";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int test_hashtable_multithread(void)
|
|
|
|
{
|
|
|
|
HT_CONFIG hash_conf = {
|
|
|
|
NULL, /* use default context */
|
|
|
|
hashtable_mt_free, /* our free function */
|
|
|
|
NULL, /* default hash function */
|
|
|
|
0, /* default hash size */
|
2024-05-27 22:49:15 +08:00
|
|
|
1, /* Check collisions */
|
2024-03-02 05:28:53 +08:00
|
|
|
};
|
|
|
|
int ret = 0;
|
2024-10-11 23:25:49 +08:00
|
|
|
thread_t workers[NUM_WORKERS];
|
2024-03-02 05:28:53 +08:00
|
|
|
int i;
|
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
struct timeval start, end, delta;
|
|
|
|
#endif
|
|
|
|
|
2024-10-11 23:25:49 +08:00
|
|
|
memset(worker_exits, 0, sizeof(char *) * NUM_WORKERS);
|
2024-03-02 05:28:53 +08:00
|
|
|
memset(test_mt_entries, 0, sizeof(TEST_MT_ENTRY) * TEST_MT_POOL_SZ);
|
2024-10-11 23:25:49 +08:00
|
|
|
memset(workers, 0, sizeof(thread_t) * NUM_WORKERS);
|
2024-03-02 05:28:53 +08:00
|
|
|
|
|
|
|
m_ht = ossl_ht_new(&hash_conf);
|
|
|
|
|
|
|
|
if (!TEST_ptr(m_ht))
|
|
|
|
goto end;
|
|
|
|
|
2024-10-11 23:13:40 +08:00
|
|
|
if (!TEST_ptr(worker_lock = CRYPTO_THREAD_lock_new()))
|
|
|
|
goto end_free;
|
|
|
|
if (!TEST_ptr(testrand_lock = CRYPTO_THREAD_lock_new()))
|
2024-03-02 05:28:53 +08:00
|
|
|
goto end_free;
|
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
#endif
|
|
|
|
|
2024-10-11 23:25:49 +08:00
|
|
|
for (i = 0; i < NUM_WORKERS; i++) {
|
2024-03-02 05:28:53 +08:00
|
|
|
if (!run_thread(&workers[i], do_mt_hash_work))
|
|
|
|
goto shutdown;
|
|
|
|
}
|
|
|
|
|
|
|
|
shutdown:
|
|
|
|
for (--i; i >= 0; i--) {
|
|
|
|
wait_for_thread(workers[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that the workers are done, check for any error
|
|
|
|
* conditions
|
|
|
|
*/
|
|
|
|
ret = 1;
|
2024-10-11 23:25:49 +08:00
|
|
|
for (i = 0; i < NUM_WORKERS; i++) {
|
2024-03-02 05:28:53 +08:00
|
|
|
if (worker_exits[i] != NULL) {
|
|
|
|
TEST_info("Worker %d failed: %s\n", i, worker_exits[i]);
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (free_failure == 1) {
|
|
|
|
TEST_info("Encountered a free failure");
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MEASURE_HASH_PERFORMANCE
|
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
timeval_subtract(&delta, &end, &start);
|
|
|
|
TEST_info("multithread stress runs 40000 ops in %ld.%ld seconds", delta.tv_sec, delta.tv_usec);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
end_free:
|
|
|
|
shutting_down = 1;
|
2024-10-11 23:13:40 +08:00
|
|
|
CRYPTO_THREAD_lock_free(worker_lock);
|
|
|
|
CRYPTO_THREAD_lock_free(testrand_lock);
|
2024-03-02 05:28:53 +08:00
|
|
|
ossl_ht_free(m_ht);
|
|
|
|
end:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-07-18 09:48:27 +08:00
|
|
|
int setup_tests(void)
|
2017-03-16 13:00:23 +08:00
|
|
|
{
|
|
|
|
ADD_TEST(test_int_lhash);
|
|
|
|
ADD_TEST(test_stress);
|
Introduce new internal hashtable implementation
Create a new hashtable that is more efficient than the existing LHASH_OF
implementation. the new ossl_ht api offers several new features that
improve performance opportunistically
* A more generalized hash function. Currently using fnv1a, provides a
more general hash function, but can still be overridden where needed
* Improved locking and reference counting. This hash table is
internally locked with an RCU lock, and optionally reference counts
elements, allowing for users to not have to create and manage their
own read/write locks
* Lockless operation. The hash table can be configured to operate
locklessly on the read side, improving performance, at the sacrifice
of the ability to grow the hash table or delete elements from it
* A filter function allowing for the retrieval of several elements at a
time matching a given criteria without having to hold a lock
permanently
* a doall_until iterator variant, that allows callers which need to
iterate over the entire hash table until a given condition is met (as
defined by the return value of the iterator callback). This allows
for callers attempting to do expensive cache searches for a small
number of elements to terminate the iteration early, saving cpu cycles
* Dynamic type safety. The hash table provides operations to set and
get data of a specific type without having to define a type at the
instatiation point
* Multiple data type storage. The hash table can store multiple data
types allowing for more flexible usage
* Ubsan safety. Because the API deals with concrete single types
(HT_KEY and HT_VALUE), leaving specific type casting to the call
recipient with dynamic type validation, this implementation is safe
from the ubsan undefined behavior warnings that require additional
thunking on callbacks.
Testing of this new hashtable with an equivalent hash function, I can
observe approximately a 6% performance improvement in the lhash_test
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/23671)
2024-01-28 23:50:38 +08:00
|
|
|
ADD_TEST(test_int_hashtable);
|
2024-08-16 21:40:43 +08:00
|
|
|
ADD_ALL_TESTS(test_hashtable_stress, 2);
|
2024-03-02 05:28:53 +08:00
|
|
|
ADD_TEST(test_hashtable_multithread);
|
2017-07-18 09:48:27 +08:00
|
|
|
return 1;
|
2017-03-16 13:00:23 +08:00
|
|
|
}
|