Skip to content

Commit

Permalink
use siphash to replace hash_string
Browse files Browse the repository at this point in the history
  • Loading branch information
dreamwind1985 committed Jan 23, 2025
1 parent 309e7aa commit 9a467a5
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 17 deletions.
2 changes: 2 additions & 0 deletions include/xquic/xquic.h
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,8 @@ typedef struct xqc_config_s {

/** bucket size of connection hash table in engine */
size_t conns_hash_bucket_size;
/* for warning when the number of elements in one bucket exceeds the value of hash_conflict_threshold*/
uint32_t hash_conflict_threshold;

/** capacity of connection priority queue in engine */
size_t conns_active_pq_capacity;
Expand Down
88 changes: 87 additions & 1 deletion src/common/xqc_str_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,21 @@
#define _XQC_STR_HASH_INCLUDED_

#include <stdint.h>
#include <time.h>

#include "src/common/xqc_str.h"
#include "src/common/xqc_siphash.h"
#include "src/common/xqc_log.h"

/*
* default log threshold of number of element in on bucket
* test result of max conflict (default 1024*1024 hash buckets) in one bucket:
* 100000 connections, the max conflict is 5
* 1000000 connections, the max conflict is 24
*/
#define XQC_HASH_DEFAULT_CONFLICT_THRESHOLD 50
/*10 second, log interval must not less then 10 second*/
#define XQC_HASH_CONFLICT_LOG_INTERVAL 10

typedef struct xqc_str_hash_element_s {
uint64_t hash;
Expand All @@ -24,20 +37,79 @@ typedef struct xqc_str_hash_table_s {
xqc_str_hash_node_t **list;
size_t count;
xqc_allocator_t allocator; /* memory allocator */
uint8_t *conflict_stat; /* statistic the number of elements in every bucket */
xqc_siphash_ctx_t siphash_ctx; /* siphash context */
uint32_t conflict_thres; /* conflict threshold in one bucket, warning if exceeded */
time_t last_log_time; /* last timestamp(second) for logging the max conflict value*/
xqc_log_t *log; /* pointer to engine's log*/
} xqc_str_hash_table_t;

/* calculate the hash value using the siphash algorithm */
static inline uint64_t
xqc_siphash_get_hash(xqc_siphash_ctx_t *ctx, const uint8_t *data, size_t len)
{
uint64_t hash_value;
if (xqc_siphash(ctx, data, len, (uint8_t *)(&hash_value), sizeof(hash_value)) == XQC_OK) {
return hash_value;
}
/*
* impossible, we set hash_size value 8 when we call xqc_siphash_init, sizeof(hash_value) is always 8
* xqc_siphash return XQC_ERROR only when hash_size not equal sizeof(hash_value), it is impossible here
*/
return 0;
}

static inline int
xqc_str_hash_init(xqc_str_hash_table_t *hash_tab, xqc_allocator_t allocator, size_t bucket_num)
xqc_str_hash_init(xqc_str_hash_table_t *hash_tab,
xqc_allocator_t allocator, size_t bucket_num,
uint32_t conflict_thres, uint8_t *key,
size_t key_len, xqc_log_t *log)
{
if (bucket_num == 0) { /* impossible */
return XQC_ERROR;
}
if (key_len != XQC_SIPHASH_KEY_SIZE) { /* siphash key length must be 16 */
return XQC_ERROR;
}
if (log == NULL) {
return XQC_ERROR;
}
xqc_memzero(hash_tab, sizeof(xqc_str_hash_table_t));
hash_tab->allocator = allocator;
hash_tab->list = allocator.malloc(allocator.opaque, sizeof(xqc_str_hash_node_t *) * bucket_num);
if (hash_tab->list == NULL) {
return XQC_ERROR;
}
xqc_memzero(hash_tab->list, sizeof(xqc_str_hash_node_t *) * bucket_num);
hash_tab->count = bucket_num;
hash_tab->conflict_stat = allocator.malloc(allocator.opaque, sizeof(uint8_t) * bucket_num);
if (hash_tab->conflict_stat == NULL) {
goto fail;
}
xqc_memzero(hash_tab->conflict_stat, sizeof(uint8_t) * bucket_num);
if (conflict_thres > 0) {
hash_tab->conflict_thres = conflict_thres;
} else {
hash_tab->conflict_thres = XQC_HASH_DEFAULT_CONFLICT_THRESHOLD;
}
hash_tab->last_log_time = 0;
hash_tab->log = log;
if (xqc_siphash_init(&hash_tab->siphash_ctx, key, key_len,
XQC_DEFAULT_HASH_SIZE, XQC_SIPHASH_C_ROUNDS,
XQC_SIPHASH_D_ROUNDS) != XQC_OK)
{
goto fail;
}
return XQC_OK;

fail:
if (hash_tab->list) {
allocator.free(allocator.opaque, hash_tab->list);
}
if (hash_tab->conflict_stat) {
allocator.free(allocator.opaque, hash_tab->conflict_stat);
}
return XQC_ERROR;
}

static inline void
Expand All @@ -53,6 +125,7 @@ xqc_str_hash_release(xqc_str_hash_table_t *hash_tab)
}
}
a->free(a->opaque, hash_tab->list);
a->free(a->opaque, hash_tab->conflict_stat);
}

static inline void *
Expand Down Expand Up @@ -90,6 +163,16 @@ xqc_str_hash_add(xqc_str_hash_table_t *hash_tab, xqc_str_hash_element_t e)

node->next = hash_tab->list[index];
hash_tab->list[index] = node;
hash_tab->conflict_stat[index] += 1;
if (hash_tab->conflict_stat[index] > hash_tab->conflict_thres) {
time_t now_sec = time(NULL);
if (now_sec >= hash_tab->last_log_time + XQC_HASH_CONFLICT_LOG_INTERVAL) {
xqc_log(hash_tab->log, XQC_LOG_WARN,
"|xqc conn hash conflict exceed|index:%ui, number of elements:%d|",
index, hash_tab->conflict_stat[index]);
hash_tab->last_log_time = now_sec;
}
}

return XQC_OK;
}
Expand All @@ -104,6 +187,9 @@ xqc_str_hash_delete(xqc_str_hash_table_t *hash_tab, uint64_t hash, xqc_str_t str
while (node) {
if (node->element.hash == hash && xqc_str_equal(str, node->element.str)) {
*pp = node->next;
if (hash_tab->conflict_stat[index] > 0) {
hash_tab->conflict_stat[index] -= 1;
}
a->free(a->opaque, node->element.str.data);
a->free(a->opaque, node);
return XQC_OK;
Expand Down
31 changes: 23 additions & 8 deletions src/transport/xqc_engine.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ xqc_config_t default_client_config = {
.conn_pool_size = 4096,
.streams_hash_bucket_size = 1024,
.conns_hash_bucket_size = 1024,
.hash_conflict_threshold = XQC_HASH_DEFAULT_CONFLICT_THRESHOLD,
.conns_active_pq_capacity = 128,
.conns_wakeup_pq_capacity = 128,
.support_version_count = 1,
Expand All @@ -62,6 +63,7 @@ xqc_config_t default_server_config = {
.conn_pool_size = 4096,
.streams_hash_bucket_size = 1024,
.conns_hash_bucket_size = 1024*1024, /* too many connections will affect lookup performance */
.hash_conflict_threshold = XQC_HASH_DEFAULT_CONFLICT_THRESHOLD,
.conns_active_pq_capacity = 1024,
.conns_wakeup_pq_capacity = 16*1024,
.support_version_count = 2,
Expand Down Expand Up @@ -94,6 +96,9 @@ xqc_set_config(xqc_config_t *dst, const xqc_config_t *src)
if (src->conns_hash_bucket_size > 0) {
dst->conns_hash_bucket_size = src->conns_hash_bucket_size;
}
if (src->hash_conflict_threshold > 0) {
dst->hash_conflict_threshold = src->hash_conflict_threshold;
}

if (src->conns_active_pq_capacity > 0) {
dst->conns_active_pq_capacity = src->conns_active_pq_capacity;
Expand Down Expand Up @@ -196,14 +201,17 @@ xqc_engine_set_log_level(xqc_engine_t *engine, xqc_log_level_t log_level)


xqc_str_hash_table_t *
xqc_engine_conns_hash_create(xqc_config_t *config)
xqc_engine_conns_hash_create(xqc_config_t *config, uint8_t *key, size_t key_len, xqc_log_t *log)
{
xqc_str_hash_table_t *hash_table = xqc_malloc(sizeof(xqc_str_hash_table_t));
if (hash_table == NULL) {
return NULL;
}

if (xqc_str_hash_init(hash_table, xqc_default_allocator, config->conns_hash_bucket_size)) {
if (xqc_str_hash_init(hash_table, xqc_default_allocator,
config->conns_hash_bucket_size, config->hash_conflict_threshold,
key, key_len, log))
{
goto fail;
}

Expand Down Expand Up @@ -270,17 +278,20 @@ xqc_engine_conns_hash_find(xqc_engine_t *engine, const xqc_cid_t *cid, char type
return NULL;
}

uint64_t hash = xqc_hash_string(cid->cid_buf, cid->cid_len);
uint64_t hash;

xqc_str_t str;
str.data = (unsigned char *)cid->cid_buf;
str.len = cid->cid_len;

if (type == 's') {
/* search by endpoint's cid */
hash = xqc_siphash_get_hash(&engine->conns_hash->siphash_ctx, cid->cid_buf, cid->cid_len);
return xqc_str_hash_find(engine->conns_hash, hash, str);

} else {
/* search by peer's cid */
hash = xqc_siphash_get_hash(&engine->conns_hash_dcid->siphash_ctx, cid->cid_buf, cid->cid_len);
xqc_conn = xqc_str_hash_find(engine->conns_hash_dcid, hash, str);
if (xqc_conn == NULL) {
xqc_log(engine->log, XQC_LOG_ERROR, "|xquic find dcid error|dcid:%s|",
Expand Down Expand Up @@ -396,6 +407,7 @@ xqc_engine_create(xqc_engine_type_t engine_type,
void *user_data)
{
xqc_engine_t *engine = NULL;
uint8_t sipkey[XQC_SIPHASH_KEY_SIZE];

/* check input parameter */
if (xqc_engine_check_config(engine_type, engine_config, ssl_config, transport_cbs)
Expand Down Expand Up @@ -443,17 +455,19 @@ xqc_engine_create(xqc_engine_type_t engine_type,
goto fail;
}

engine->conns_hash = xqc_engine_conns_hash_create(engine->config);
xqc_get_random(engine->rand_generator, sipkey, sizeof(sipkey));

engine->conns_hash = xqc_engine_conns_hash_create(engine->config, sipkey, sizeof(sipkey), engine->log);
if (engine->conns_hash == NULL) {
goto fail;
}

engine->conns_hash_dcid = xqc_engine_conns_hash_create(engine->config);
engine->conns_hash_dcid = xqc_engine_conns_hash_create(engine->config, sipkey, sizeof(sipkey), engine->log);
if (engine->conns_hash_dcid == NULL) {
goto fail;
}

engine->conns_hash_sr_token = xqc_engine_conns_hash_create(engine->config);
engine->conns_hash_sr_token = xqc_engine_conns_hash_create(engine->config, sipkey, sizeof(sipkey), engine->log);
if (engine->conns_hash_sr_token == NULL) {
goto fail;
}
Expand Down Expand Up @@ -996,7 +1010,8 @@ xqc_engine_handle_stateless_reset(xqc_engine_t *engine,
return -XQC_ERROR;
}

hash = xqc_hash_string(sr_token, XQC_STATELESS_RESET_TOKENLEN);
hash = xqc_siphash_get_hash(&engine->conns_hash_sr_token->siphash_ctx,
sr_token, XQC_STATELESS_RESET_TOKENLEN);
str.data = (unsigned char *)sr_token;
str.len = XQC_STATELESS_RESET_TOKENLEN;

Expand Down Expand Up @@ -1540,4 +1555,4 @@ xqc_engine_remove_active_queue(xqc_engine_t *engine, xqc_connection_t *conn)
conn->conn_flag &= ~XQC_CONN_FLAG_TICKING;
}
return XQC_OK;
}
}
16 changes: 8 additions & 8 deletions src/transport/xqc_utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include "src/common/xqc_log.h"
#include "src/transport/xqc_utils.h"
#include "src/transport/xqc_conn.h"

#include "src/common/xqc_time.h"

int
xqc_conns_pq_push(xqc_pq_t *pq, xqc_connection_t *conn, uint64_t time_us)
Expand Down Expand Up @@ -59,7 +59,7 @@ int
xqc_insert_conns_hash(xqc_str_hash_table_t *conns_hash, xqc_connection_t *conn,
const uint8_t *data, size_t len)
{
uint64_t hash = xqc_hash_string(data, len);
uint64_t hash = xqc_siphash_get_hash(&conns_hash->siphash_ctx, data, len);

xqc_str_hash_element_t c = {
.str = {
Expand All @@ -70,7 +70,7 @@ xqc_insert_conns_hash(xqc_str_hash_table_t *conns_hash, xqc_connection_t *conn,
.value = conn
};

if (xqc_str_hash_add(conns_hash, c)) {
if (xqc_str_hash_add(conns_hash, c) != XQC_OK) {
return -XQC_EMALLOC;
}
return 0;
Expand All @@ -80,7 +80,7 @@ int
xqc_remove_conns_hash(xqc_str_hash_table_t *conns_hash, xqc_connection_t *conn,
const uint8_t *data, size_t len)
{
uint64_t hash = xqc_hash_string(data, len);
uint64_t hash = xqc_siphash_get_hash(&conns_hash->siphash_ctx, data, len);
xqc_str_t str = {
.data = (unsigned char *)data,
.len = len,
Expand All @@ -97,7 +97,7 @@ int
xqc_insert_conns_addr_hash(xqc_str_hash_table_t *conns_hash, xqc_connection_t *conn,
const struct sockaddr *addr, socklen_t addrlen)
{
uint64_t hash = xqc_hash_string((unsigned char *)addr, addrlen);
uint64_t hash = xqc_siphash_get_hash(&conns_hash->siphash_ctx, (const uint8_t *)addr, addrlen);
xqc_str_hash_element_t c = {
.str = {
.data = (unsigned char *)addr,
Expand All @@ -107,7 +107,7 @@ xqc_insert_conns_addr_hash(xqc_str_hash_table_t *conns_hash, xqc_connection_t *c
.value = conn
};

if (xqc_str_hash_add(conns_hash, c)) {
if (xqc_str_hash_add(conns_hash, c) != XQC_OK) {
return -XQC_EMALLOC;
}
return 0;
Expand All @@ -118,11 +118,11 @@ void *
xqc_find_conns_hash(xqc_str_hash_table_t *conns_hash, xqc_connection_t *conn,
const uint8_t *data, size_t len)
{
uint64_t hash = xqc_hash_string(data, len);
uint64_t hash = xqc_siphash_get_hash(&conns_hash->siphash_ctx, data, len);
xqc_str_t str = {
.data = (unsigned char *)data,
.len = len,
};

return xqc_str_hash_find(conns_hash, hash, str);
}
}

0 comments on commit 9a467a5

Please sign in to comment.