db354bd2e1
Sketching algorithm provide high-fidelity approximate measurements and appears as a promising alternative to traditional approaches such as packet sampling. NitroSketch [1] is a software sketching framework that optimizes performance, provides accuracy guarantees, and supports a variety of sketches. This commit adds a new data structure called sketch into membership library. This new data structure is an efficient way to profile the traffic for heavy hitters. Also use min-heap structure to maintain the top-k flow keys. [1] Zaoxing Liu, Ran Ben-Basat, Gil Einziger, Yaron Kassner, Vladimir Braverman, Roy Friedman, Vyas Sekar, "NitroSketch: Robust and General Sketch-based Monitoring in Software Switches", in ACM SIGCOMM 2019. https://dl.acm.org/doi/pdf/10.1145/3341302.3342076 Signed-off-by: Alan Liu <zaoxingliu@gmail.com> Signed-off-by: Yipeng Wang <yipeng1.wang@intel.com> Signed-off-by: Leyi Rong <leyi.rong@intel.com> Tested-by: Yu Jiang <yux.jiang@intel.com>
425 lines
9.8 KiB
C
425 lines
9.8 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2020 Intel Corporation
|
|
* Copyright(c) 2020, Alan Liu <zaoxingliu@gmail.com>
|
|
*/
|
|
|
|
#ifndef RTE_MEMBER_HEAP_H
|
|
#define RTE_MEMBER_HEAP_H
|
|
|
|
#include <rte_ring_elem.h>
|
|
#include "rte_member.h"
|
|
|
|
#define LCHILD(x) (2 * x + 1)
|
|
#define RCHILD(x) (2 * x + 2)
|
|
#define PARENT(x) ((x - 1) / 2)
|
|
|
|
#define HASH_BKT_SIZE 16
|
|
#define HASH_HP_MULTI 4
|
|
#define HASH_RESIZE_MULTI 2
|
|
|
|
struct hash_bkt {
|
|
uint16_t sig[HASH_BKT_SIZE];
|
|
uint16_t idx[HASH_BKT_SIZE];
|
|
};
|
|
|
|
struct hash {
|
|
uint16_t bkt_cnt;
|
|
uint16_t num_item;
|
|
uint32_t seed;
|
|
struct hash_bkt buckets[0];
|
|
};
|
|
|
|
struct node {
|
|
void *key;
|
|
uint64_t count;
|
|
};
|
|
|
|
struct minheap {
|
|
uint32_t key_len;
|
|
uint32_t size;
|
|
uint32_t socket;
|
|
struct hash *hashtable;
|
|
struct node *elem;
|
|
};
|
|
|
|
static int
|
|
hash_table_insert(const void *key, int value, int key_len, struct hash *table)
|
|
{
|
|
uint32_t hash = MEMBER_HASH_FUNC(key, key_len, table->seed);
|
|
uint16_t idx = hash % table->bkt_cnt;
|
|
uint16_t sig = hash >> 16;
|
|
int i;
|
|
|
|
for (i = 0; i < HASH_BKT_SIZE; i++) {
|
|
if (table->buckets[idx].idx[i] == 0) {
|
|
table->buckets[idx].idx[i] = value;
|
|
table->buckets[idx].sig[i] = sig;
|
|
table->num_item++;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int
|
|
hash_table_update(const void *key, int old_value, int value, int key_len, struct hash *table)
|
|
{
|
|
uint32_t hash = MEMBER_HASH_FUNC(key, key_len, table->seed);
|
|
uint16_t idx = hash % table->bkt_cnt;
|
|
uint16_t sig = hash >> 16;
|
|
int i;
|
|
|
|
for (i = 0; i < HASH_BKT_SIZE; i++) {
|
|
if (table->buckets[idx].sig[i] == sig && table->buckets[idx].idx[i] == old_value) {
|
|
table->buckets[idx].idx[i] = value;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
hash_table_del(const void *key, uint16_t value, int key_len, struct hash *table)
|
|
{
|
|
uint32_t hash = MEMBER_HASH_FUNC(key, key_len, table->seed);
|
|
uint16_t idx = hash % table->bkt_cnt;
|
|
uint16_t sig = hash >> 16;
|
|
int i;
|
|
|
|
for (i = 0; i < HASH_BKT_SIZE; i++) {
|
|
if (table->buckets[idx].sig[i] == sig && table->buckets[idx].idx[i] == value) {
|
|
table->buckets[idx].idx[i] = 0;
|
|
table->num_item--;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
hash_table_lookup(const void *key, int key_len, struct minheap *hp)
|
|
{
|
|
struct hash *table = hp->hashtable;
|
|
uint32_t hash = MEMBER_HASH_FUNC(key, key_len, table->seed);
|
|
uint16_t idx = hash % table->bkt_cnt;
|
|
uint16_t sig = hash >> 16;
|
|
int i;
|
|
|
|
for (i = 0; i < HASH_BKT_SIZE; i++) {
|
|
if (table->buckets[idx].sig[i] == sig && table->buckets[idx].idx[i] != 0) {
|
|
uint32_t hp_idx = table->buckets[idx].idx[i] - 1;
|
|
|
|
if (memcmp(hp->elem[hp_idx].key, key, hp->key_len) == 0)
|
|
return hp_idx;
|
|
}
|
|
}
|
|
|
|
return -ENOENT; /* key doesn't exist */
|
|
}
|
|
|
|
static int
|
|
resize_hash_table(struct minheap *hp)
|
|
{
|
|
uint32_t i;
|
|
uint32_t new_bkt_cnt;
|
|
|
|
while (1) {
|
|
new_bkt_cnt = hp->hashtable->bkt_cnt * HASH_RESIZE_MULTI;
|
|
|
|
RTE_MEMBER_LOG(ERR, "Sketch Minheap HT load factor is [%f]\n",
|
|
hp->hashtable->num_item / ((float)hp->hashtable->bkt_cnt * HASH_BKT_SIZE));
|
|
RTE_MEMBER_LOG(ERR, "Sketch Minheap HT resize happen!\n");
|
|
rte_free(hp->hashtable);
|
|
hp->hashtable = rte_zmalloc_socket(NULL, sizeof(struct hash) +
|
|
new_bkt_cnt * sizeof(struct hash_bkt),
|
|
RTE_CACHE_LINE_SIZE, hp->socket);
|
|
|
|
if (hp->hashtable == NULL) {
|
|
RTE_MEMBER_LOG(ERR, "Sketch Minheap HT allocation failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
hp->hashtable->bkt_cnt = new_bkt_cnt;
|
|
|
|
for (i = 0; i < hp->size; ++i) {
|
|
if (hash_table_insert(hp->elem[i].key,
|
|
i + 1, hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR,
|
|
"Sketch Minheap HT resize insert fail!\n");
|
|
break;
|
|
}
|
|
}
|
|
if (i == hp->size)
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* find the item in the given minheap */
|
|
static int
|
|
rte_member_minheap_find(struct minheap *hp, const void *key)
|
|
{
|
|
int idx = hash_table_lookup(key, hp->key_len, hp);
|
|
return idx;
|
|
}
|
|
|
|
static int
|
|
rte_member_minheap_init(struct minheap *heap, int size,
|
|
uint32_t socket, uint32_t seed)
|
|
{
|
|
heap->elem = rte_zmalloc_socket(NULL, sizeof(struct node) * size,
|
|
RTE_CACHE_LINE_SIZE, socket);
|
|
if (heap->elem == NULL) {
|
|
RTE_MEMBER_LOG(ERR, "Sketch Minheap elem allocation failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
uint32_t hash_bkt_cnt = rte_align32pow2(size * HASH_HP_MULTI) / HASH_BKT_SIZE;
|
|
|
|
if (hash_bkt_cnt == 0)
|
|
hash_bkt_cnt = 1;
|
|
|
|
heap->hashtable = rte_zmalloc_socket(NULL, sizeof(struct hash) +
|
|
hash_bkt_cnt * sizeof(struct hash_bkt),
|
|
RTE_CACHE_LINE_SIZE, socket);
|
|
|
|
if (heap->hashtable == NULL) {
|
|
RTE_MEMBER_LOG(ERR, "Sketch Minheap HT allocation failed\n");
|
|
rte_free(heap->elem);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
heap->hashtable->seed = seed;
|
|
heap->hashtable->bkt_cnt = hash_bkt_cnt;
|
|
heap->socket = socket;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* swap the minheap nodes */
|
|
static __rte_always_inline void
|
|
rte_member_heap_swap(struct node *n1, struct node *n2)
|
|
{
|
|
struct node temp = *n1;
|
|
*n1 = *n2;
|
|
*n2 = temp;
|
|
}
|
|
|
|
/* heapify function */
|
|
static void
|
|
rte_member_heapify(struct minheap *hp, uint32_t idx, bool update_hash)
|
|
{
|
|
uint32_t smallest;
|
|
|
|
if (LCHILD(idx) < hp->size &&
|
|
hp->elem[LCHILD(idx)].count < hp->elem[idx].count)
|
|
smallest = LCHILD(idx);
|
|
else
|
|
smallest = idx;
|
|
|
|
if (RCHILD(idx) < hp->size &&
|
|
hp->elem[RCHILD(idx)].count < hp->elem[smallest].count)
|
|
smallest = RCHILD(idx);
|
|
|
|
if (smallest != idx) {
|
|
rte_member_heap_swap(&(hp->elem[idx]), &(hp->elem[smallest]));
|
|
|
|
if (update_hash) {
|
|
if (hash_table_update(hp->elem[smallest].key, idx + 1, smallest + 1,
|
|
hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table update failed\n");
|
|
return;
|
|
}
|
|
|
|
if (hash_table_update(hp->elem[idx].key, smallest + 1, idx + 1,
|
|
hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table update failed\n");
|
|
return;
|
|
}
|
|
}
|
|
rte_member_heapify(hp, smallest, update_hash);
|
|
}
|
|
}
|
|
|
|
/* insert a node into the minheap */
|
|
static int
|
|
rte_member_minheap_insert_node(struct minheap *hp, const void *key,
|
|
int counter, void *key_slot,
|
|
struct rte_ring *free_key_slot)
|
|
{
|
|
struct node nd;
|
|
uint32_t slot_id;
|
|
|
|
if (rte_ring_sc_dequeue_elem(free_key_slot, &slot_id, sizeof(uint32_t)) != 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap get empty keyslot failed\n");
|
|
return -1;
|
|
}
|
|
|
|
nd.count = counter;
|
|
nd.key = RTE_PTR_ADD(key_slot, slot_id * hp->key_len);
|
|
|
|
memcpy(nd.key, key, hp->key_len);
|
|
|
|
uint32_t i = (hp->size)++;
|
|
|
|
while (i && nd.count < hp->elem[PARENT(i)].count) {
|
|
hp->elem[i] = hp->elem[PARENT(i)];
|
|
if (hash_table_update(hp->elem[i].key, PARENT(i) + 1, i + 1,
|
|
hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table update failed\n");
|
|
return -1;
|
|
}
|
|
i = PARENT(i);
|
|
}
|
|
hp->elem[i] = nd;
|
|
|
|
if (hash_table_insert(key, i + 1, hp->key_len, hp->hashtable) < 0) {
|
|
if (resize_hash_table(hp) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table resize failed\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* delete a key from the minheap */
|
|
static int
|
|
rte_member_minheap_delete_node(struct minheap *hp, const void *key,
|
|
void *key_slot, struct rte_ring *free_key_slot)
|
|
{
|
|
int idx = rte_member_minheap_find(hp, key);
|
|
uint32_t offset = RTE_PTR_DIFF(hp->elem[idx].key, key_slot) / hp->key_len;
|
|
|
|
if (hash_table_del(key, idx + 1, hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table delete failed\n");
|
|
return -1;
|
|
}
|
|
|
|
rte_ring_sp_enqueue_elem(free_key_slot, &offset, sizeof(uint32_t));
|
|
|
|
if (idx == (int)(hp->size - 1)) {
|
|
hp->size--;
|
|
return 0;
|
|
}
|
|
|
|
hp->elem[idx] = hp->elem[hp->size - 1];
|
|
|
|
if (hash_table_update(hp->elem[idx].key, hp->size, idx + 1,
|
|
hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table update failed\n");
|
|
return -1;
|
|
}
|
|
hp->size--;
|
|
rte_member_heapify(hp, idx, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* replace a min node with a new key. */
|
|
static int
|
|
rte_member_minheap_replace_node(struct minheap *hp,
|
|
const void *new_key,
|
|
int new_counter)
|
|
{
|
|
struct node nd;
|
|
void *recycle_key = NULL;
|
|
|
|
recycle_key = hp->elem[0].key;
|
|
|
|
if (hash_table_del(recycle_key, 1, hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table delete failed\n");
|
|
return -1;
|
|
}
|
|
|
|
hp->elem[0] = hp->elem[hp->size - 1];
|
|
|
|
if (hash_table_update(hp->elem[0].key, hp->size, 1,
|
|
hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table update failed\n");
|
|
return -1;
|
|
}
|
|
hp->size--;
|
|
|
|
rte_member_heapify(hp, 0, true);
|
|
|
|
nd.count = new_counter;
|
|
nd.key = recycle_key;
|
|
|
|
memcpy(nd.key, new_key, hp->key_len);
|
|
|
|
uint32_t i = (hp->size)++;
|
|
|
|
while (i && nd.count < hp->elem[PARENT(i)].count) {
|
|
hp->elem[i] = hp->elem[PARENT(i)];
|
|
if (hash_table_update(hp->elem[i].key, PARENT(i) + 1, i + 1,
|
|
hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table update failed\n");
|
|
return -1;
|
|
}
|
|
i = PARENT(i);
|
|
}
|
|
|
|
hp->elem[i] = nd;
|
|
|
|
if (hash_table_insert(new_key, i + 1, hp->key_len, hp->hashtable) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table replace insert failed\n");
|
|
if (resize_hash_table(hp) < 0) {
|
|
RTE_MEMBER_LOG(ERR, "Minheap Hash Table replace resize failed\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* sort the heap into a descending array */
|
|
static void
|
|
rte_member_heapsort(struct minheap *hp, struct node *result_array)
|
|
{
|
|
struct minheap new_hp;
|
|
|
|
/* build a new heap for using the given array */
|
|
new_hp.size = hp->size;
|
|
new_hp.key_len = hp->key_len;
|
|
new_hp.elem = result_array;
|
|
memcpy(result_array, hp->elem, hp->size * sizeof(struct node));
|
|
|
|
/* sort the new heap */
|
|
while (new_hp.size > 1) {
|
|
rte_member_heap_swap(&(new_hp.elem[0]), &(new_hp.elem[new_hp.size - 1]));
|
|
new_hp.size--;
|
|
rte_member_heapify(&new_hp, 0, false);
|
|
}
|
|
}
|
|
|
|
static void
|
|
rte_member_minheap_free(struct minheap *hp)
|
|
{
|
|
if (hp == NULL)
|
|
return;
|
|
|
|
rte_free(hp->elem);
|
|
rte_free(hp->hashtable);
|
|
}
|
|
|
|
static void
|
|
rte_member_minheap_reset(struct minheap *hp)
|
|
{
|
|
if (hp == NULL)
|
|
return;
|
|
|
|
memset(hp->elem, 0, sizeof(struct node) * hp->size);
|
|
hp->size = 0;
|
|
|
|
memset((char *)hp->hashtable + sizeof(struct hash), 0,
|
|
hp->hashtable->bkt_cnt * sizeof(struct hash_bkt));
|
|
hp->hashtable->num_item = 0;
|
|
}
|
|
|
|
#endif /* RTE_MEMBER_HEAP_H */
|