DPDK——哈希库使用

5 篇文章 2 订阅

一、概述

最近项目中使用了DPDK作为底层框架,有个需求想借助DPDK的哈希库,于是便调研了下相关使用,本文将介绍哈希库的简单使用,使用的是DPDP19.08版本。

首先我们打开DPDK的说明文档(https://doc.dpdk.org/api-19.08/rte__hash_8h.html),其主要的函数接口如下:

1.1 建立哈希表

类似于文件操作,首先咱得创建一个哈希表,得到一个类似于句柄的东东:

struct rte_hash* rte_hash_create(const struct rte_hash_parameters *params)	

这里需要定义一个struct rte_hash_parameters的参数,其结构如下:

struct rte_hash_parameters {
	const char *name;		/**< Name of the hash. */
	uint32_t entries;		/**< Total hash table entries. */
	uint32_t reserved;		/**< Unused field. Should be set to 0 */
	uint32_t key_len;		/**< Length of hash key. */
	rte_hash_function hash_func;	/**< Primary Hash function used to calculate hash. */
	uint32_t hash_func_init_val;	/**< Init value used by hash_func. */
	int socket_id;			/**< NUMA Socket ID for memory. */
	uint8_t extra_flag;		/**< Indicate if additional parameters are present. */
};

其中,entries也就是哈希表的条目数最大支持1<<30(1,073,741,824)大小,extra_flag可以设置多线程和扩展桶操作,具体操作有以下几种:

1.RTE_HASH_EXTRA_FLAGS_MULTI_WRITER_ADD:插入的默认行为,允许多个线程写入哈希表,如果仅设置此标志,无法保护“读者”免受正在进行的写入操作的影响。
2.RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY:支持读写并发,使用读写锁提供并发。
3.RTE_HASH_EXTRA_FLAGS_EXT_TABLE:哈希桶将使用链表进行扩展,以插入这些失败的键。 对于插入哈希表的键个数能达到容量的100%且不能容忍任何键插入失败(即使很少)的工作负载,此功能非常重要。
4.RTE_HASH_EXTRA_FLAGS_NO_FREE_ON_DEL:在调用delete()时不会释放哈希表中条目的位置,当启用RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF 时默认启用此功能。
5.RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY_LF:提供不使用读写锁的读/写并发,目前该设置不支持可扩展存储桶表功能。
6.RTE_HASH_EXTRA_FLAGS_TRANS_MEM_SUPPORT:启用硬件事务内存支持,如果硬件支持,读写锁将使用硬件事务性内存(例如Intel®TSX)来保证线程安全。如果平台支持Intel®TSX,建议设置事务性内存标志,因为这将加快并发表操作。否则,由于软件锁定机制相关的开销,并发操作将变慢。

2.2 增、改

int32_t
rte_hash_add_key(const struct rte_hash *h, const void *key);

int
rte_hash_add_key_data(const struct rte_hash *h, const void *key, void *data);

int32_t
rte_hash_add_key_with_hash_data(const struct rte_hash *h, const void *key,
						hash_sig_t sig, void *data);

int32_t
rte_hash_add_key_with_hash_data(const struct rte_hash *h, const void *key,
						hash_sig_t sig, void *data);
int32_t
rte_hash_del_key(const struct rte_hash *h, const void *key);

2.3 删

int32_t
rte_hash_del_key(const struct rte_hash *h, const void *key);

int32_t
rte_hash_del_key_with_hash(const struct rte_hash *h, const void *key, hash_sig_t sig);

2.4 查

int
rte_hash_get_key_with_position(const struct rte_hash *h, const int32_t position,
			       void **key);

int
rte_hash_lookup_data(const struct rte_hash *h, const void *key, void **data);

int
rte_hash_lookup_with_hash_data(const struct rte_hash *h, const void *key,
					hash_sig_t sig, void **data);
int32_t
rte_hash_lookup(const struct rte_hash *h, const void *key);

int32_t
rte_hash_lookup_with_hash(const struct rte_hash *h,
				const void *key, hash_sig_t sig);

hash_sig_t
rte_hash_hash(const struct rte_hash *h, const void *key);

int
rte_hash_lookup_bulk_data(const struct rte_hash *h, const void **keys,
		      uint32_t num_keys, uint64_t *hit_mask, void *data[]);

int
rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys,
		      uint32_t num_keys, int32_t *positions);    

int32_t
rte_hash_iterate(const struct rte_hash *h, const void **key, void **data, uint32_t *next);    

二、实现

上述给出了大部分的函数接口,下面我们借助其中的一部分函数测试下200万键值对的存放和查找效率,C代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

#include <rte_hash.h>
#include <rte_jhash.h>
#include <rte_hash_crc.h>
#include <rte_mbuf.h>
#include <rte_mempool.h>

#define MAX_NUM 2000000

struct rte_hash *create_hash()
{
    struct rte_hash *pstHash;
	const struct rte_hash_parameters stHashParam =
	{
		.name = "hash_test",
		.entries = (1 << 24),
		.reserved = 0,
		.key_len = 16,
		.hash_func = rte_jhash,
		.hash_func_init_val = 0,
		.socket_id = rte_socket_id(),
		.extra_flag = 0,
	};
	
	pstHash = rte_hash_create(&stHashParam);
    if (!pstHash) {
        printf("rte_hash_create() fail!\n");
        return NULL;
    }

    printf("create hash_table: [%s] success!\n", stHashParam.name);

    return pstHash;
}

void dbg_print(char *info, unsigned char *dat, int dat_len)
{
    int i;
    
    printf("\n%s:%d\n", info, dat_len);
    for (i = 0; i < dat_len; i++)
    {
        if (i && (i % 16 == 0))
            printf("\n");
        printf("%02x ", dat[i]);
    }
    printf("\n");
}

int main(int argc, char **argv)
{
    struct rte_hash *pstHash;
    void *data = NULL;
    char acSupi[15];
    int *iData = NULL;
    int iRet = 0;
    int iSize = 0;
    float time_s, time_ns, time_ms;
    struct timespec ts1, ts2; 
    int i = 0;

	iRet = rte_eal_init(argc, argv);
	if(iRet < 0)
		rte_exit(EXIT_FAILURE, "rte_eal_init()\n");

    pstHash = create_hash();
    
    iData = (int*)malloc(sizeof(int) * MAX_NUM);
    if(iData == NULL)
		return -1;

#if 1
     //clock_gettime(CLOCK_MONOTONIC, &ts1); //记录函数开始时间
    
    // 测试200万存储时间
    memset(acSupi, 0x00, sizeof acSupi);
    for(i = 0; i < MAX_NUM; ++i)
    {
        iData[i] = i + 1;
        sprintf(acSupi, "%d", i);
        iRet = rte_hash_add_key_data(pstHash, acSupi, (void*)&iData[i]);
		
		/* 
        if(i % 100000 == 0)
        {   
            dbg_print("acSupi", (unsigned char *)acSupi, sizeof(acSupi));
            printf("acSupi : %s\n", acSupi);
            printf("iRet = %d, iData = %d\n", iRet, iData);
        }
		*/
    }
#endif

#if 1
    // 测试200万查找效率
    clock_gettime(CLOCK_MONOTONIC, &ts1); //记录函数开始时间
    memset(acSupi, 0x00, sizeof acSupi);
    for(i = 0; i < MAX_NUM; ++i)
    {
        sprintf(acSupi, "%d", i);
        iRet = rte_hash_lookup_data(pstHash, acSupi, &data);
		/*
		if(i % 100000 == 0)
		{  
		   dbg_print("acSupi", (unsigned char *)acSupi, sizeof(acSupi));
			   printf("iRet = %d, data = %d\n", iRet, *(int *)data);
		}
		*/
    }

#endif

	clock_gettime(CLOCK_MONOTONIC, &ts2);           // 记录函数结束时间
    time_s = ts2.tv_sec - ts1.tv_sec;               // 得到秒的时间
    time_ns = (ts2.tv_nsec - ts1.tv_nsec);          // 得到纳秒的时间
    time_ms = time_s * 1000 + time_ns / 1000000;
	
    printf("\n*****************************************\n");
	iSize = rte_hash_count(pstHash);
    printf("htable size = %d\n", iSize);
    printf("run time is %f ms \n", time_ms);
	printf("\n*****************************************\n");
	

    return 0;
}

在虚拟机测试单线程200万连续存储和查找均不到1秒:

在这里插入图片描述在这里插入图片描述

注意!!!
我在测试的犯了一个很粗心的错误,rte_hash_add_key_data (const struct rte_hash *h, const void *key, void *data)这个函数在存储data时,是以变量的地址为准,而我存储200万data时,只是对同一个变量设置不同的值来存入哈希表,这样导致后面使用rte_hash_lookup_data (const struct rte_hash *h, const void *key, void **data)时得到的data内容都是同一个值!

参考资料:
后台服务器:https://course.0voice.com/v1/course/intro?courseId=5&agentId=0
https://doc.dpdk.org/api-19.08/rte__hash_8h.html
https://blog.csdn.net/qq_15437629/article/details/112989017?spm=1001.2101.3001.6650.7&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-7.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-7.pc_relevant_paycolumn_v3&utm_relevant_index=9

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿杰的小鱼塘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值