一個帶衝突率檢查、超時機制的雜湊表
阿新 • • 發佈:2019-02-08
相關特性:
- 開鏈法解決衝突問題
- 雜湊函式和比較函式通過介面方式提供
- 支援雜湊表查詢、插入、刪除等操作
- 雜湊表是最大容量的7倍,衝突率較低(4%以下)
- 超時機制,刪除失效元素
- 統計衝突率,方便除錯
- 動態分配記憶體,釋放的記憶體被新增空閒列表,降低malloc函式呼叫次數
用法:
首先建立雜湊表,
htable_t* htable_create(uint32_t payload_size, uint32_t cnt_used_max,
uint32_t cnt_idle_init, uint32_t cnt_idle_max,
uint32_t cnt_timeout, uint32_t (*hash)(const void*),
int (*equal)(const void*, const void*));
payload_size為每個資料的大小,cnt_used_max是評估最大容量,cnt_idle_init是預分配的空閒容量,cnt_idle_max是空閒容量的最大值
cnt_timeout是超時時間,如果為0,則不進行超時檢查,hash為雜湊函式,equal為比較函式。
然後就可以進行插入、查詢、刪除等操作,
htable_item_t* htable_find(htable_t *table, void *key, void *payload);
htable_item_t* htable_insert(htable_t *table, void *key, void *payload);
int htable_remove(htable_t *table, htable_item_t *item);
另外還可以進行定期刪除超時失效的資料項,定期更新當前時間,
void htable_remove_timeout(htable_t *table);
void htable_update_now(htable_t *table, time_t now);
定期列印統計資訊
main.c
- 開鏈法解決衝突問題
- 雜湊函式和比較函式通過介面方式提供
- 支援雜湊表查詢、插入、刪除等操作
- 雜湊表是最大容量的7倍,衝突率較低(4%以下)
- 超時機制,刪除失效元素
- 統計衝突率,方便除錯
- 動態分配記憶體,釋放的記憶體被新增空閒列表,降低malloc函式呼叫次數
用法:
首先建立雜湊表,
htable_t* htable_create(uint32_t payload_size, uint32_t cnt_used_max,
uint32_t cnt_idle_init, uint32_t cnt_idle_max,
uint32_t cnt_timeout, uint32_t (*hash)(const void*),
int (*equal)(const void*, const void*));
payload_size為每個資料的大小,cnt_used_max是評估最大容量,cnt_idle_init是預分配的空閒容量,cnt_idle_max是空閒容量的最大值
cnt_timeout是超時時間,如果為0,則不進行超時檢查,hash為雜湊函式,equal為比較函式。
然後就可以進行插入、查詢、刪除等操作,
htable_item_t* htable_find(htable_t *table, void *key, void *payload);
htable_item_t* htable_insert(htable_t *table, void *key, void *payload);
int htable_remove(htable_t *table, htable_item_t *item);
另外還可以進行定期刪除超時失效的資料項,定期更新當前時間,
void htable_remove_timeout(htable_t *table);
void htable_update_now(htable_t *table, time_t now);
定期列印統計資訊
void htable_print_stat(htable_t *table);
原始碼如下:
htable.c
htable.h#include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "htable.h" inline int likely(int expr) { #ifdef __GNUC__ return __builtin_expect(expr, 1); #else return expr; #endif } inline int unlikely(int expr) { #ifdef __GNUC__ return __builtin_expect(expr, 0); #else return expr; #endif } static inline uint32_t __htable_factor(uint32_t max) { uint32_t i = 0; uint32_t value = 0; do { value = (uint32_t)sqrt(max); for (i = 2; i <= value; i++) { if (max % i == 0) { break; } } if (i > value) { return max; } }while (--max); return 0; } void htable_destory(htable_t *table) { htable_item_t *item; htable_item_t *_item; if (NULL == table) { return; } for (item = table->freed; item != NULL;) { _item = item; item = item->next; free(_item); } for (item = table->used_head; item != NULL;) { _item = item; item = item->next; free(_item); } free(table); } htable_t* htable_create(uint32_t payload_size, uint32_t cnt_used_max, uint32_t cnt_idle_init, uint32_t cnt_idle_max, uint32_t cnt_timeout, uint32_t (*hash)(const void*), int (*equal)(const void*, const void*)) { uint32_t cnt_hash = __htable_factor(cnt_used_max * HTABLE_FACTOR); htable_t *table = NULL; htable_item_t *item = NULL; int i = 0; // alloc hash table struct if (NULL == (table = (htable_t *)calloc(1, sizeof(htable_t) + cnt_hash * sizeof(htable_item_t *)))) { return NULL; } // init freed list for (i = 0; i < cnt_idle_init; i++) { if (NULL == (item = (htable_item_t *)calloc(1, sizeof(htable_item_t) + payload_size))) { goto err; } if (table->freed != NULL) { table->freed->prev = item; } item->next = table->freed; item->prev = NULL; table->freed = item; } table->payload_size = payload_size; table->cnt_used_max = cnt_used_max; table->cnt_idle_max = cnt_idle_max; table->cnt_hash = cnt_hash; table->cnt_used = 0; table->cnt_idle = cnt_idle_init; table->cnt_conflict = 0; table->hash = hash; table->equal = equal; table->cnt_timeout = cnt_timeout; return table; err: htable_destory(table); return NULL; } htable_item_t* htable_find(htable_t *table, void *key, void *payload) { htable_item_t *item = NULL; uint32_t idx = 0; if (unlikely(table == NULL || key == NULL || payload == NULL)) { return NULL; } idx = table->hash(key) % table->cnt_hash; for (item = table->heads[idx]; item != NULL; item = item->next) { if (likely(table->equal(item->payload, payload))) { // update used list if (likely(item->prev1 != NULL)) { if (likely(item->next1 != NULL)) { item->next1->prev1 = item->prev1; } else { table->used_tail = item->prev1; } item->prev1->next1 = item->next1; item->next1 = table->used_head; item->prev1 = NULL; table->used_head->prev1 = item; table->used_head = item; } item->last_read = table->now; return item; } } return NULL; } htable_item_t* htable_insert(htable_t *table, void *key, void *payload) { htable_item_t *item = NULL; uint32_t idx = 0; if (unlikely(table == NULL || key == NULL || payload == NULL)) { return NULL; } // used count limit check if (table->cnt_used >= table->cnt_used_max) { table->cnt_exceed_err++; return NULL; } // get one free item if (table->freed == NULL) { if (NULL == (item = (htable_item_t *)calloc(1, sizeof(htable_item_t) + table->payload_size))) { return NULL; } table->cnt_alloc++; } else { item = table->freed; table->freed = table->freed->next; table->cnt_idle--; } // set used list for timeout checking item->last_read = table->now; item->next1 = table->used_head; item->prev1 = NULL; if (table->used_head != NULL) { table->used_head->prev1 = item; } else { table->used_tail = item; } table->used_head = item; // set hash table list idx = table->hash(key) % table->cnt_hash; item->idx = idx; item->next = table->heads[idx]; item->prev = NULL; if (table->heads[idx] != NULL) { table->heads[idx]->prev = item; table->cnt_conflict++; } table->heads[idx] = item; table->cnt_used++; memcpy(item->payload, payload, table->payload_size); return item; } static inline void _htable_remove(htable_t *table, htable_item_t *item) { // update hash table if (item->next != NULL || item->prev != NULL) { table->cnt_conflict--; } if (item->next != NULL) { item->next->prev = item->prev; } if (item->prev != NULL) { item->prev->next = item->next; } else { table->heads[item->idx] = item->next; } // update used list for timeout checking if (item->next1 != NULL) { item->next1->prev1 = item->prev1; } else { table->used_tail = item->prev1; } if (item->prev1 != NULL) { item->prev1->next1 = item->next1; } else { table->used_head = item->next1; } // release item or insert item to freed list table->cnt_used--; if (table->cnt_idle >= table->cnt_idle_max) { free(item); } else { item->next = table->freed; table->freed = item; table->cnt_idle++; } } int htable_remove(htable_t *table, htable_item_t *item) { if (unlikely(table == NULL || item == NULL)) { return -1; } _htable_remove(table, item); return 0; } void htable_remove_timeout(htable_t *table) { htable_item_t *item; if (table == NULL || table->cnt_timeout == 0) { return; } while (NULL != (item = table->used_tail)) { // remain items are not timeout if (table->now < item->last_read + table->cnt_timeout) { break; } _htable_remove(table, item); } } void htable_update_now(htable_t *table, time_t now) { table->now = now; } void htable_print_stat(htable_t *table) { printf("htable stat. used: %u, idle: %u, conflict: %u, exceed_err: %u, alloc: %u\n", table->cnt_used, table->cnt_idle, table->cnt_conflict, table->cnt_exceed_err, table->cnt_alloc); table->cnt_alloc = 0; table->cnt_exceed_err = 0; }
#ifndef _HTABLE_H_ #define _HTABLE_H_ #include <stdint.h> #include <time.h> #ifdef __cplusplus extern "C" { #endif #define HTABLE_FACTOR 7 typedef struct _htable_item { struct _htable_item* next; struct _htable_item* prev; struct _htable_item* next1; // for timeout check struct _htable_item* prev1; // for timeout check time_t last_read; // last read time uint32_t idx; // hash table idx char payload[0]; // payload } htable_item_t; typedef struct { uint32_t payload_size; // payload size uint32_t cnt_used_max; // max of used item count uint32_t cnt_idle_max; // max idle item count uint32_t cnt_hash; // hash domain size uint32_t cnt_used; // used item count uint32_t cnt_idle; // idle item count uint32_t cnt_conflict; // conflict item count uint32_t cnt_exceed_err; // cnt_used exceed cnt_used_max uint32_t cnt_alloc; // alloc count uint32_t cnt_timeout; // how many seconds timeout, 0 mean not check timeout time_t now; // current time uint32_t (*hash)(const void*); // hash function int (*equal)(const void*, const void*); // item equal function htable_item_t *used_head; // for timeout check, insert from this htable_item_t *used_tail; // for timeout check, remove from this htable_item_t *freed; // freed list (singly linked list) htable_item_t *heads[0]; // hash table array } htable_t; htable_t* htable_create(uint32_t payload_size, uint32_t cnt_used_max, uint32_t cnt_idle_init, uint32_t cnt_idle_max, uint32_t cnt_timeout, uint32_t (*hash)(const void*), int (*equal)(const void*, const void*)); void htable_destory(htable_t *table); htable_item_t* htable_find(htable_t *table, void *key, void *payload); htable_item_t* htable_insert(htable_t *table, void *key, void *payload); int htable_remove(htable_t *table, htable_item_t *item); void htable_remove_timeout(htable_t *table); void htable_update_now(htable_t *table, time_t now); void htable_print_stat(htable_t *table); #ifdef __cplusplus }; #endif #endif
main.c
// gcc main.c htable.c -lm -g -Wall -O2
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "htable.h"
uint32_t ip_key(const void* key)
{
return *(uint32_t *)key;
}
int ip_cmp(const void* value1, const void* value2)
{
return *(uint32_t *)value1 == *(uint32_t *)value2;
}
int main()
{
htable_t *table = NULL;
int i = 0;
int j = 0;
uint32_t ip = 0;
if (NULL == (table = htable_create(4, 3000 * 1000, 1000, 22000, 60, ip_key, ip_cmp))) {
return -1;
}
srand(time(NULL));
while (1)
{
htable_update_now(table, time(NULL));
for (i = 0; i < 10000; i++) {
ip = (uint32_t)rand();
if (NULL == htable_find(table, &ip, &ip)) {
htable_insert(table, &ip, &ip);
}
}
htable_remove_timeout(table);
if ((j++ % 20) == 0) {
htable_print_stat(table);
}
sleep(1);
}
htable_destory(table);
return 0;
}