From patchwork Fri Sep 21 17:17:29 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Yipeng1" X-Patchwork-Id: 45151 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 866A64F9C; Sat, 22 Sep 2018 02:22:26 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by dpdk.org (Postfix) with ESMTP id F01F61DA4 for ; Sat, 22 Sep 2018 02:22:12 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 21 Sep 2018 17:22:10 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,287,1534834800"; d="scan'208";a="76346597" Received: from skx-yipeng.jf.intel.com ([10.54.81.175]) by orsmga006.jf.intel.com with ESMTP; 21 Sep 2018 17:22:10 -0700 From: Yipeng Wang To: bruce.richardson@intel.com Cc: dev@dpdk.org, yipeng1.wang@intel.com, michel@digirati.com.br, honnappa.nagarahalli@arm.com Date: Fri, 21 Sep 2018 10:17:29 -0700 Message-Id: <1537550255-252066-2-git-send-email-yipeng1.wang@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> References: <1536253745-133104-1-git-send-email-yipeng1.wang@intel.com> <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> Subject: [dpdk-dev] [PATCH v2 1/7] test/hash: fix bucket size in hash perf test X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" The bucket size was changed from 4 to 8 but the corresponding perf test was not changed accordingly. Fixes: 58017c98ed53 ("hash: add vectorized comparison") Cc: stable@dpdk.org Signed-off-by: Yipeng Wang --- test/test/test_hash_perf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test/test_hash_perf.c b/test/test/test_hash_perf.c index 33dcb9f..9ed7125 100644 --- a/test/test/test_hash_perf.c +++ b/test/test/test_hash_perf.c @@ -20,7 +20,7 @@ #define MAX_ENTRIES (1 << 19) #define KEYS_TO_ADD (MAX_ENTRIES * 3 / 4) /* 75% table utilization */ #define NUM_LOOKUPS (KEYS_TO_ADD * 5) /* Loop among keys added, several times */ -#define BUCKET_SIZE 4 +#define BUCKET_SIZE 8 #define NUM_BUCKETS (MAX_ENTRIES / BUCKET_SIZE) #define MAX_KEYSIZE 64 #define NUM_KEYSIZES 10 From patchwork Fri Sep 21 17:17:30 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Yipeng1" X-Patchwork-Id: 45149 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 2B7934CA6; Sat, 22 Sep 2018 02:22:22 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by dpdk.org (Postfix) with ESMTP id 539931DA4 for ; Sat, 22 Sep 2018 02:22:12 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 21 Sep 2018 17:22:10 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,287,1534834800"; d="scan'208";a="76346598" Received: from skx-yipeng.jf.intel.com ([10.54.81.175]) by orsmga006.jf.intel.com with ESMTP; 21 Sep 2018 17:22:10 -0700 From: Yipeng Wang To: bruce.richardson@intel.com Cc: dev@dpdk.org, yipeng1.wang@intel.com, michel@digirati.com.br, honnappa.nagarahalli@arm.com Date: Fri, 21 Sep 2018 10:17:30 -0700 Message-Id: <1537550255-252066-3-git-send-email-yipeng1.wang@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> References: <1536253745-133104-1-git-send-email-yipeng1.wang@intel.com> <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> Subject: [dpdk-dev] [PATCH v2 2/7] test/hash: more accurate hash perf test output X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Edit the printf information when error happens to be more accurate and informative. Signed-off-by: Yipeng Wang Acked-by: Bruce Richardson --- test/test/test_hash_perf.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/test/test_hash_perf.c b/test/test/test_hash_perf.c index 9ed7125..4d00c20 100644 --- a/test/test/test_hash_perf.c +++ b/test/test/test_hash_perf.c @@ -248,7 +248,7 @@ timed_adds(unsigned with_hash, unsigned with_data, unsigned table_index) (const void *) keys[i], signatures[i], data); if (ret < 0) { - printf("Failed to add key number %u\n", ret); + printf("H+D: Failed to add key number %u\n", i); return -1; } } else if (with_hash && !with_data) { @@ -258,7 +258,7 @@ timed_adds(unsigned with_hash, unsigned with_data, unsigned table_index) if (ret >= 0) positions[i] = ret; else { - printf("Failed to add key number %u\n", ret); + printf("H: Failed to add key number %u\n", i); return -1; } } else if (!with_hash && with_data) { @@ -266,7 +266,7 @@ timed_adds(unsigned with_hash, unsigned with_data, unsigned table_index) (const void *) keys[i], data); if (ret < 0) { - printf("Failed to add key number %u\n", ret); + printf("D: Failed to add key number %u\n", i); return -1; } } else { @@ -274,7 +274,7 @@ timed_adds(unsigned with_hash, unsigned with_data, unsigned table_index) if (ret >= 0) positions[i] = ret; else { - printf("Failed to add key number %u\n", ret); + printf("Failed to add key number %u\n", i); return -1; } } @@ -442,7 +442,7 @@ timed_deletes(unsigned with_hash, unsigned with_data, unsigned table_index) if (ret >= 0) positions[i] = ret; else { - printf("Failed to add key number %u\n", ret); + printf("Failed to delete key number %u\n", i); return -1; } } From patchwork Fri Sep 21 17:17:31 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Yipeng1" X-Patchwork-Id: 45150 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 7C20B4D3A; Sat, 22 Sep 2018 02:22:24 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by dpdk.org (Postfix) with ESMTP id B590EA49 for ; Sat, 22 Sep 2018 02:22:12 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 21 Sep 2018 17:22:10 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,287,1534834800"; d="scan'208";a="76346601" Received: from skx-yipeng.jf.intel.com ([10.54.81.175]) by orsmga006.jf.intel.com with ESMTP; 21 Sep 2018 17:22:10 -0700 From: Yipeng Wang To: bruce.richardson@intel.com Cc: dev@dpdk.org, yipeng1.wang@intel.com, michel@digirati.com.br, honnappa.nagarahalli@arm.com Date: Fri, 21 Sep 2018 10:17:31 -0700 Message-Id: <1537550255-252066-4-git-send-email-yipeng1.wang@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> References: <1536253745-133104-1-git-send-email-yipeng1.wang@intel.com> <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> Subject: [dpdk-dev] [PATCH v2 3/7] test/hash: fix rw test with non-consecutive cores X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" the multi-reader and multi-writer rte_hash unit test does not work correctly with non-consicutive core ids. This commit fixes the issue. Fixes: 0eb3726ebcf1 ("test/hash: add test for read/write concurrency") Cc: stable@dpdk.org Signed-off-by: Yipeng Wang Tested-by: Bruce Richardson Acked-by: Bruce Richardson --- test/test/test_hash_readwrite.c | 78 ++++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/test/test/test_hash_readwrite.c b/test/test/test_hash_readwrite.c index 55ae33d..2a4f7b9 100644 --- a/test/test/test_hash_readwrite.c +++ b/test/test/test_hash_readwrite.c @@ -24,6 +24,7 @@ #define NUM_TEST 3 unsigned int core_cnt[NUM_TEST] = {2, 4, 8}; +unsigned int slave_core_ids[RTE_MAX_LCORE]; struct perf { uint32_t single_read; uint32_t single_write; @@ -60,12 +61,15 @@ test_hash_readwrite_worker(__attribute__((unused)) void *arg) uint64_t begin, cycles; int ret; - offset = (lcore_id - rte_get_master_lcore()) - * tbl_rw_test_param.num_insert; + for (i = 0; i < rte_lcore_count(); i++) { + if (slave_core_ids[i] == lcore_id) + break; + } + offset = tbl_rw_test_param.num_insert * i; printf("Core #%d inserting and reading %d: %'"PRId64" - %'"PRId64"\n", lcore_id, tbl_rw_test_param.num_insert, - offset, offset + tbl_rw_test_param.num_insert); + offset, offset + tbl_rw_test_param.num_insert - 1); begin = rte_rdtsc_precise(); @@ -171,6 +175,7 @@ test_hash_readwrite_functional(int use_htm) uint32_t duplicated_keys = 0; uint32_t lost_keys = 0; int use_jhash = 1; + int slave_cnt = rte_lcore_count() - 1; rte_atomic64_init(&gcycles); rte_atomic64_clear(&gcycles); @@ -182,17 +187,17 @@ test_hash_readwrite_functional(int use_htm) goto err; tbl_rw_test_param.num_insert = - TOTAL_INSERT / rte_lcore_count(); + TOTAL_INSERT / slave_cnt; tbl_rw_test_param.rounded_tot_insert = tbl_rw_test_param.num_insert - * rte_lcore_count(); + * slave_cnt; printf("++++++++Start function tests:+++++++++\n"); /* Fire all threads. */ rte_eal_mp_remote_launch(test_hash_readwrite_worker, - NULL, CALL_MASTER); + NULL, SKIP_MASTER); rte_eal_mp_wait_lcore(); while (rte_hash_iterate(tbl_rw_test_param.h, &next_key, @@ -249,7 +254,7 @@ test_hash_readwrite_functional(int use_htm) } static int -test_rw_reader(__attribute__((unused)) void *arg) +test_rw_reader(void *arg) { uint64_t i; uint64_t begin, cycles; @@ -276,7 +281,7 @@ test_rw_reader(__attribute__((unused)) void *arg) } static int -test_rw_writer(__attribute__((unused)) void *arg) +test_rw_writer(void *arg) { uint64_t i; uint32_t lcore_id = rte_lcore_id(); @@ -285,8 +290,13 @@ test_rw_writer(__attribute__((unused)) void *arg) uint64_t start_coreid = (uint64_t)(uintptr_t)arg; uint64_t offset; - offset = TOTAL_INSERT / 2 + (lcore_id - start_coreid) - * tbl_rw_test_param.num_insert; + for (i = 0; i < rte_lcore_count(); i++) { + if (slave_core_ids[i] == lcore_id) + break; + } + + offset = TOTAL_INSERT / 2 + (i - (start_coreid)) * + tbl_rw_test_param.num_insert; begin = rte_rdtsc_precise(); for (i = offset; i < offset + tbl_rw_test_param.num_insert; i++) { ret = rte_hash_add_key_data(tbl_rw_test_param.h, @@ -384,8 +394,8 @@ test_hash_readwrite_perf(struct perf *perf_results, int use_htm, perf_results->single_read = end / i; for (n = 0; n < NUM_TEST; n++) { - unsigned int tot_lcore = rte_lcore_count(); - if (tot_lcore < core_cnt[n] * 2 + 1) + unsigned int tot_slave_lcore = rte_lcore_count() - 1; + if (tot_slave_lcore < core_cnt[n] * 2) goto finish; rte_atomic64_clear(&greads); @@ -415,17 +425,19 @@ test_hash_readwrite_perf(struct perf *perf_results, int use_htm, */ /* Test only reader cases */ - for (i = 1; i <= core_cnt[n]; i++) + for (i = 0; i < core_cnt[n]; i++) rte_eal_remote_launch(test_rw_reader, - (void *)(uintptr_t)read_cnt, i); + (void *)(uintptr_t)read_cnt, + slave_core_ids[i]); rte_eal_mp_wait_lcore(); start_coreid = i; /* Test only writer cases */ - for (; i <= core_cnt[n] * 2; i++) + for (; i < core_cnt[n] * 2; i++) rte_eal_remote_launch(test_rw_writer, - (void *)((uintptr_t)start_coreid), i); + (void *)((uintptr_t)start_coreid), + slave_core_ids[i]); rte_eal_mp_wait_lcore(); @@ -464,22 +476,26 @@ test_hash_readwrite_perf(struct perf *perf_results, int use_htm, } } - start_coreid = core_cnt[n] + 1; + start_coreid = core_cnt[n]; if (reader_faster) { - for (i = core_cnt[n] + 1; i <= core_cnt[n] * 2; i++) + for (i = core_cnt[n]; i < core_cnt[n] * 2; i++) rte_eal_remote_launch(test_rw_writer, - (void *)((uintptr_t)start_coreid), i); - for (i = 1; i <= core_cnt[n]; i++) + (void *)((uintptr_t)start_coreid), + slave_core_ids[i]); + for (i = 0; i < core_cnt[n]; i++) rte_eal_remote_launch(test_rw_reader, - (void *)(uintptr_t)read_cnt, i); + (void *)(uintptr_t)read_cnt, + slave_core_ids[i]); } else { - for (i = 1; i <= core_cnt[n]; i++) + for (i = 0; i < core_cnt[n]; i++) rte_eal_remote_launch(test_rw_reader, - (void *)(uintptr_t)read_cnt, i); - for (; i <= core_cnt[n] * 2; i++) + (void *)(uintptr_t)read_cnt, + slave_core_ids[i]); + for (; i < core_cnt[n] * 2; i++) rte_eal_remote_launch(test_rw_writer, - (void *)((uintptr_t)start_coreid), i); + (void *)((uintptr_t)start_coreid), + slave_core_ids[i]); } rte_eal_mp_wait_lcore(); @@ -562,13 +578,19 @@ test_hash_readwrite_main(void) * writer threads for performance numbers. */ int use_htm, reader_faster; + unsigned int i = 0, core_id = 0; - if (rte_lcore_count() == 1) { - printf("More than one lcore is required " + if (rte_lcore_count() <= 2) { + printf("More than two lcores are required " "to do read write test\n"); return 0; } + RTE_LCORE_FOREACH_SLAVE(core_id) { + slave_core_ids[i] = core_id; + i++; + } + setlocale(LC_NUMERIC, ""); if (rte_tm_supported()) { @@ -610,8 +632,6 @@ test_hash_readwrite_main(void) printf("Results summary:\n"); - int i; - printf("single read: %u\n", htm_results.single_read); printf("single write: %u\n", htm_results.single_write); for (i = 0; i < NUM_TEST; i++) { From patchwork Fri Sep 21 17:17:32 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Yipeng1" X-Patchwork-Id: 45152 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id EF2914CA5; Sat, 22 Sep 2018 02:22:28 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by dpdk.org (Postfix) with ESMTP id 70F33A49 for ; Sat, 22 Sep 2018 02:22:13 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 21 Sep 2018 17:22:10 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,287,1534834800"; d="scan'208";a="76346604" Received: from skx-yipeng.jf.intel.com ([10.54.81.175]) by orsmga006.jf.intel.com with ESMTP; 21 Sep 2018 17:22:10 -0700 From: Yipeng Wang To: bruce.richardson@intel.com Cc: dev@dpdk.org, yipeng1.wang@intel.com, michel@digirati.com.br, honnappa.nagarahalli@arm.com Date: Fri, 21 Sep 2018 10:17:32 -0700 Message-Id: <1537550255-252066-5-git-send-email-yipeng1.wang@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> References: <1536253745-133104-1-git-send-email-yipeng1.wang@intel.com> <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> Subject: [dpdk-dev] [PATCH v2 4/7] hash: fix unnecessary code X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Since the depth-first search of cuckoo path is removed, we do not need the macro anymore which specifies the depth of the cuckoo search. Fixes: f2e3001b53ec ("hash: support read/write concurrency") Cc: stable@dpdk.org Signed-off-by: Yipeng Wang Acked-by: Bruce Richardson --- lib/librte_hash/rte_cuckoo_hash.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/librte_hash/rte_cuckoo_hash.h b/lib/librte_hash/rte_cuckoo_hash.h index b43f467..fc0e5c2 100644 --- a/lib/librte_hash/rte_cuckoo_hash.h +++ b/lib/librte_hash/rte_cuckoo_hash.h @@ -104,8 +104,6 @@ const rte_hash_cmp_eq_t cmp_jump_table[NUM_KEY_CMP_CASES] = { #define LCORE_CACHE_SIZE 64 -#define RTE_HASH_MAX_PUSHES 100 - #define RTE_HASH_BFS_QUEUE_MAX_LEN 1000 #define RTE_XABORT_CUCKOO_PATH_INVALIDED 0x4 From patchwork Fri Sep 21 17:17:33 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Yipeng1" X-Patchwork-Id: 45154 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id E364A58EC; Sat, 22 Sep 2018 02:22:32 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by dpdk.org (Postfix) with ESMTP id 865CF4C9F for ; Sat, 22 Sep 2018 02:22:13 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 21 Sep 2018 17:22:11 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,287,1534834800"; d="scan'208";a="76346607" Received: from skx-yipeng.jf.intel.com ([10.54.81.175]) by orsmga006.jf.intel.com with ESMTP; 21 Sep 2018 17:22:10 -0700 From: Yipeng Wang To: bruce.richardson@intel.com Cc: dev@dpdk.org, yipeng1.wang@intel.com, michel@digirati.com.br, honnappa.nagarahalli@arm.com Date: Fri, 21 Sep 2018 10:17:33 -0700 Message-Id: <1537550255-252066-6-git-send-email-yipeng1.wang@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> References: <1536253745-133104-1-git-send-email-yipeng1.wang@intel.com> <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> Subject: [dpdk-dev] [PATCH v2 5/7] hash: add extendable bucket feature X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" In use cases that hash table capacity needs to be guaranteed, the extendable bucket feature can be used to contain extra keys in linked lists when conflict happens. This is similar concept to the extendable bucket hash table in packet framework. This commit adds the extendable bucket feature. User can turn it on or off through the extra flag field during table creation time. Extendable bucket table composes of buckets that can be linked list to current main table. When extendable bucket is enabled, the table utilization can always acheive 100%. Although keys ending up in the ext buckets may have longer look up time, they should be rare due to the cuckoo algorithm. Signed-off-by: Yipeng Wang --- lib/librte_hash/rte_cuckoo_hash.c | 326 +++++++++++++++++++++++++++++++++----- lib/librte_hash/rte_cuckoo_hash.h | 5 + lib/librte_hash/rte_hash.h | 3 + 3 files changed, 292 insertions(+), 42 deletions(-) diff --git a/lib/librte_hash/rte_cuckoo_hash.c b/lib/librte_hash/rte_cuckoo_hash.c index f7b86c8..616900b 100644 --- a/lib/librte_hash/rte_cuckoo_hash.c +++ b/lib/librte_hash/rte_cuckoo_hash.c @@ -31,6 +31,10 @@ #include "rte_hash.h" #include "rte_cuckoo_hash.h" +#define FOR_EACH_BUCKET(CURRENT_BKT, START_BUCKET) \ + for (CURRENT_BKT = START_BUCKET; \ + CURRENT_BKT != NULL; \ + CURRENT_BKT = CURRENT_BKT->next) TAILQ_HEAD(rte_hash_list, rte_tailq_entry); @@ -63,6 +67,14 @@ rte_hash_find_existing(const char *name) return h; } +static inline struct rte_hash_bucket * +rte_hash_get_last_bkt(struct rte_hash_bucket *lst_bkt) +{ + while (lst_bkt->next != NULL) + lst_bkt = lst_bkt->next; + return lst_bkt; +} + void rte_hash_set_cmp_func(struct rte_hash *h, rte_hash_cmp_eq_t func) { h->cmp_jump_table_idx = KEY_CUSTOM; @@ -85,13 +97,17 @@ rte_hash_create(const struct rte_hash_parameters *params) struct rte_tailq_entry *te = NULL; struct rte_hash_list *hash_list; struct rte_ring *r = NULL; + struct rte_ring *r_ext = NULL; char hash_name[RTE_HASH_NAMESIZE]; void *k = NULL; void *buckets = NULL; + void *buckets_ext = NULL; char ring_name[RTE_RING_NAMESIZE]; + char ext_ring_name[RTE_RING_NAMESIZE]; unsigned num_key_slots; unsigned i; unsigned int hw_trans_mem_support = 0, multi_writer_support = 0; + unsigned int ext_table_support = 0; unsigned int readwrite_concur_support = 0; rte_hash_function default_hash_func = (rte_hash_function)rte_jhash; @@ -124,6 +140,9 @@ rte_hash_create(const struct rte_hash_parameters *params) multi_writer_support = 1; } + if (params->extra_flag & RTE_HASH_EXTRA_FLAGS_EXT_TABLE) + ext_table_support = 1; + /* Store all keys and leave the first entry as a dummy entry for lookup_bulk */ if (multi_writer_support) /* @@ -145,6 +164,24 @@ rte_hash_create(const struct rte_hash_parameters *params) goto err; } + const uint32_t num_buckets = rte_align32pow2(params->entries) / + RTE_HASH_BUCKET_ENTRIES; + + snprintf(ext_ring_name, sizeof(ext_ring_name), "HT_EXT_%s", + params->name); + /* Create ring for extendable buckets. */ + if (ext_table_support) { + r_ext = rte_ring_create(ext_ring_name, + rte_align32pow2(num_buckets + 1), + params->socket_id, 0); + + if (r_ext == NULL) { + RTE_LOG(ERR, HASH, "ext buckets memory allocation " + "failed\n"); + goto err; + } + } + snprintf(hash_name, sizeof(hash_name), "HT_%s", params->name); rte_rwlock_write_lock(RTE_EAL_TAILQ_RWLOCK); @@ -177,18 +214,34 @@ rte_hash_create(const struct rte_hash_parameters *params) goto err_unlock; } - const uint32_t num_buckets = rte_align32pow2(params->entries) - / RTE_HASH_BUCKET_ENTRIES; - buckets = rte_zmalloc_socket(NULL, num_buckets * sizeof(struct rte_hash_bucket), RTE_CACHE_LINE_SIZE, params->socket_id); if (buckets == NULL) { - RTE_LOG(ERR, HASH, "memory allocation failed\n"); + RTE_LOG(ERR, HASH, "buckets memory allocation failed\n"); goto err_unlock; } + /* Allocate same number of extendable buckets */ + if (ext_table_support) { + buckets_ext = rte_zmalloc_socket(NULL, + num_buckets * sizeof(struct rte_hash_bucket), + RTE_CACHE_LINE_SIZE, params->socket_id); + if (buckets_ext == NULL) { + RTE_LOG(ERR, HASH, "ext buckets memory allocation " + "failed\n"); + goto err_unlock; + } + /* Populate ext bkt ring. We reserve 0 similar to the + * key-data slot, just in case in future we want to + * use bucket index for the linked list and 0 means NULL + * for next bucket + */ + for (i = 1; i <= num_buckets; i++) + rte_ring_sp_enqueue(r_ext, (void *)((uintptr_t) i)); + } + const uint32_t key_entry_size = sizeof(struct rte_hash_key) + params->key_len; const uint64_t key_tbl_size = (uint64_t) key_entry_size * num_key_slots; @@ -262,6 +315,8 @@ rte_hash_create(const struct rte_hash_parameters *params) h->num_buckets = num_buckets; h->bucket_bitmask = h->num_buckets - 1; h->buckets = buckets; + h->buckets_ext = buckets_ext; + h->free_ext_bkts = r_ext; h->hash_func = (params->hash_func == NULL) ? default_hash_func : params->hash_func; h->key_store = k; @@ -269,6 +324,7 @@ rte_hash_create(const struct rte_hash_parameters *params) h->hw_trans_mem_support = hw_trans_mem_support; h->multi_writer_support = multi_writer_support; h->readwrite_concur_support = readwrite_concur_support; + h->ext_table_support = ext_table_support; #if defined(RTE_ARCH_X86) if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2)) @@ -304,9 +360,11 @@ rte_hash_create(const struct rte_hash_parameters *params) rte_rwlock_write_unlock(RTE_EAL_TAILQ_RWLOCK); err: rte_ring_free(r); + rte_ring_free(r_ext); rte_free(te); rte_free(h); rte_free(buckets); + rte_free(buckets_ext); rte_free(k); return NULL; } @@ -344,6 +402,7 @@ rte_hash_free(struct rte_hash *h) rte_free(h->readwrite_lock); } rte_ring_free(h->free_slots); + rte_ring_free(h->free_ext_bkts); rte_free(h->key_store); rte_free(h->buckets); rte_free(h); @@ -403,7 +462,6 @@ __hash_rw_writer_lock(const struct rte_hash *h) rte_rwlock_write_lock(h->readwrite_lock); } - static inline void __hash_rw_reader_lock(const struct rte_hash *h) { @@ -448,6 +506,14 @@ rte_hash_reset(struct rte_hash *h) while (rte_ring_dequeue(h->free_slots, &ptr) == 0) rte_pause(); + /* clear free extendable bucket ring and memory */ + if (h->ext_table_support) { + memset(h->buckets_ext, 0, h->num_buckets * + sizeof(struct rte_hash_bucket)); + while (rte_ring_dequeue(h->free_ext_bkts, &ptr) == 0) + rte_pause(); + } + /* Repopulate the free slots ring. Entry zero is reserved for key misses */ if (h->multi_writer_support) tot_ring_cnt = h->entries + (RTE_MAX_LCORE - 1) * @@ -458,6 +524,12 @@ rte_hash_reset(struct rte_hash *h) for (i = 1; i < tot_ring_cnt + 1; i++) rte_ring_sp_enqueue(h->free_slots, (void *)((uintptr_t) i)); + /* Repopulate the free ext bkt ring. */ + if (h->ext_table_support) + for (i = 1; i < h->num_buckets + 1; i++) + rte_ring_sp_enqueue(h->free_ext_bkts, + (void *)((uintptr_t) i)); + if (h->multi_writer_support) { /* Reset local caches per lcore */ for (i = 0; i < RTE_MAX_LCORE; i++) @@ -524,24 +596,27 @@ rte_hash_cuckoo_insert_mw(const struct rte_hash *h, int32_t *ret_val) { unsigned int i; - struct rte_hash_bucket *cur_bkt = prim_bkt; + struct rte_hash_bucket *cur_bkt; int32_t ret; __hash_rw_writer_lock(h); /* Check if key was inserted after last check but before this * protected region in case of inserting duplicated keys. */ - ret = search_and_update(h, data, key, cur_bkt, sig, alt_hash); + ret = search_and_update(h, data, key, prim_bkt, sig, alt_hash); if (ret != -1) { __hash_rw_writer_unlock(h); *ret_val = ret; return 1; } - ret = search_and_update(h, data, key, sec_bkt, alt_hash, sig); - if (ret != -1) { - __hash_rw_writer_unlock(h); - *ret_val = ret; - return 1; + + FOR_EACH_BUCKET(cur_bkt, sec_bkt) { + ret = search_and_update(h, data, key, cur_bkt, alt_hash, sig); + if (ret != -1) { + __hash_rw_writer_unlock(h); + *ret_val = ret; + return 1; + } } /* Insert new entry if there is room in the primary @@ -580,7 +655,7 @@ rte_hash_cuckoo_move_insert_mw(const struct rte_hash *h, int32_t *ret_val) { uint32_t prev_alt_bkt_idx; - struct rte_hash_bucket *cur_bkt = bkt; + struct rte_hash_bucket *cur_bkt; struct queue_node *prev_node, *curr_node = leaf; struct rte_hash_bucket *prev_bkt, *curr_bkt = leaf->bkt; uint32_t prev_slot, curr_slot = leaf_slot; @@ -597,18 +672,20 @@ rte_hash_cuckoo_move_insert_mw(const struct rte_hash *h, /* Check if key was inserted after last check but before this * protected region. */ - ret = search_and_update(h, data, key, cur_bkt, sig, alt_hash); + ret = search_and_update(h, data, key, bkt, sig, alt_hash); if (ret != -1) { __hash_rw_writer_unlock(h); *ret_val = ret; return 1; } - ret = search_and_update(h, data, key, alt_bkt, alt_hash, sig); - if (ret != -1) { - __hash_rw_writer_unlock(h); - *ret_val = ret; - return 1; + FOR_EACH_BUCKET(cur_bkt, alt_bkt) { + ret = search_and_update(h, data, key, cur_bkt, alt_hash, sig); + if (ret != -1) { + __hash_rw_writer_unlock(h); + *ret_val = ret; + return 1; + } } while (likely(curr_node->prev != NULL)) { @@ -711,15 +788,18 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, { hash_sig_t alt_hash; uint32_t prim_bucket_idx, sec_bucket_idx; - struct rte_hash_bucket *prim_bkt, *sec_bkt; + struct rte_hash_bucket *prim_bkt, *sec_bkt, *cur_bkt; struct rte_hash_key *new_k, *keys = h->key_store; void *slot_id = NULL; - uint32_t new_idx; + void *ext_bkt_id = NULL; + uint32_t new_idx, bkt_id; int ret; unsigned n_slots; unsigned lcore_id; + unsigned int i; struct lcore_cache *cached_free_slots = NULL; int32_t ret_val; + struct rte_hash_bucket *last; prim_bucket_idx = sig & h->bucket_bitmask; prim_bkt = &h->buckets[prim_bucket_idx]; @@ -739,10 +819,12 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, } /* Check if key is already inserted in secondary location */ - ret = search_and_update(h, data, key, sec_bkt, alt_hash, sig); - if (ret != -1) { - __hash_rw_writer_unlock(h); - return ret; + FOR_EACH_BUCKET(cur_bkt, sec_bkt) { + ret = search_and_update(h, data, key, cur_bkt, alt_hash, sig); + if (ret != -1) { + __hash_rw_writer_unlock(h); + return ret; + } } __hash_rw_writer_unlock(h); @@ -808,10 +890,71 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, else if (ret == 1) { enqueue_slot_back(h, cached_free_slots, slot_id); return ret_val; - } else { + } + + /* if ext table not enabled, we failed the insertion */ + if (!h->ext_table_support) { enqueue_slot_back(h, cached_free_slots, slot_id); return ret; } + + /* Now we need to go through the extendable bucket. Protection is needed + * to protect all extendable bucket processes. + */ + __hash_rw_writer_lock(h); + /* We check for duplicates again since could be inserted before the lock */ + ret = search_and_update(h, data, key, prim_bkt, sig, alt_hash); + if (ret != -1) { + enqueue_slot_back(h, cached_free_slots, slot_id); + goto failure; + } + + FOR_EACH_BUCKET(cur_bkt, sec_bkt) { + ret = search_and_update(h, data, key, cur_bkt, alt_hash, sig); + if (ret != -1) { + enqueue_slot_back(h, cached_free_slots, slot_id); + goto failure; + } + } + + /* Search extendable buckets to find an empty entry to insert. */ + struct rte_hash_bucket *next_bkt = sec_bkt->next; + FOR_EACH_BUCKET(cur_bkt, next_bkt) { + for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { + /* Check if slot is available */ + if (likely(cur_bkt->key_idx[i] == EMPTY_SLOT)) { + cur_bkt->sig_current[i] = alt_hash; + cur_bkt->sig_alt[i] = sig; + cur_bkt->key_idx[i] = new_idx; + __hash_rw_writer_unlock(h); + return new_idx - 1; + } + } + } + + /* Failed to get an empty entry from extendable buckets. Link a new + * extendable bucket. We first get a free bucket from ring. + */ + if (rte_ring_sc_dequeue(h->free_ext_bkts, &ext_bkt_id) != 0) { + ret = -ENOSPC; + goto failure; + } + + bkt_id = (uint32_t)((uintptr_t)ext_bkt_id) - 1; + /* Use the first location of the new bucket */ + (h->buckets_ext[bkt_id]).sig_current[0] = alt_hash; + (h->buckets_ext[bkt_id]).sig_alt[0] = sig; + (h->buckets_ext[bkt_id]).key_idx[0] = new_idx; + /* Link the new bucket to sec bucket linked list */ + last = rte_hash_get_last_bkt(sec_bkt); + last->next = &h->buckets_ext[bkt_id]; + __hash_rw_writer_unlock(h); + return new_idx - 1; + +failure: + __hash_rw_writer_unlock(h); + return ret; + } int32_t @@ -890,7 +1033,7 @@ __rte_hash_lookup_with_hash(const struct rte_hash *h, const void *key, { uint32_t bucket_idx; hash_sig_t alt_hash; - struct rte_hash_bucket *bkt; + struct rte_hash_bucket *bkt, *cur_bkt; int ret; bucket_idx = sig & h->bucket_bitmask; @@ -910,10 +1053,12 @@ __rte_hash_lookup_with_hash(const struct rte_hash *h, const void *key, bkt = &h->buckets[bucket_idx]; /* Check if key is in secondary location */ - ret = search_one_bucket(h, key, alt_hash, data, bkt); - if (ret != -1) { - __hash_rw_reader_unlock(h); - return ret; + FOR_EACH_BUCKET(cur_bkt, bkt) { + ret = search_one_bucket(h, key, alt_hash, data, cur_bkt); + if (ret != -1) { + __hash_rw_reader_unlock(h); + return ret; + } } __hash_rw_reader_unlock(h); return -ENOENT; @@ -1015,15 +1160,17 @@ __rte_hash_del_key_with_hash(const struct rte_hash *h, const void *key, { uint32_t bucket_idx; hash_sig_t alt_hash; - struct rte_hash_bucket *bkt; - int32_t ret; + struct rte_hash_bucket *prim_bkt, *sec_bkt; + struct rte_hash_bucket *cur_bkt, *prev_bkt, *next_bkt; + int32_t ret, i; + struct rte_hash_bucket *tobe_removed_bkt = NULL; bucket_idx = sig & h->bucket_bitmask; - bkt = &h->buckets[bucket_idx]; + prim_bkt = &h->buckets[bucket_idx]; __hash_rw_writer_lock(h); /* look for key in primary bucket */ - ret = search_and_remove(h, key, bkt, sig); + ret = search_and_remove(h, key, prim_bkt, sig); if (ret != -1) { __hash_rw_writer_unlock(h); return ret; @@ -1032,17 +1179,51 @@ __rte_hash_del_key_with_hash(const struct rte_hash *h, const void *key, /* Calculate secondary hash */ alt_hash = rte_hash_secondary_hash(sig); bucket_idx = alt_hash & h->bucket_bitmask; - bkt = &h->buckets[bucket_idx]; + sec_bkt = &h->buckets[bucket_idx]; /* look for key in secondary bucket */ - ret = search_and_remove(h, key, bkt, alt_hash); + ret = search_and_remove(h, key, sec_bkt, alt_hash); if (ret != -1) { __hash_rw_writer_unlock(h); return ret; } + /* Not in main table, we need to search ext buckets */ + if (h->ext_table_support) { + next_bkt = sec_bkt->next; + FOR_EACH_BUCKET(cur_bkt, next_bkt) { + ret = search_and_remove(h, key, cur_bkt, alt_hash); + if (ret != -1) + goto return_bkt; + } + } + __hash_rw_writer_unlock(h); return -ENOENT; + +/* Search extendable buckets to see if any empty bucket need to be recycled */ +return_bkt: + for (cur_bkt = sec_bkt->next, prev_bkt = sec_bkt; cur_bkt != NULL; + prev_bkt = cur_bkt, cur_bkt = cur_bkt->next) { + for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { + if (cur_bkt->key_idx[i] != EMPTY_SLOT) + break; + } + if (i == RTE_HASH_BUCKET_ENTRIES) { + prev_bkt->next = cur_bkt->next; + cur_bkt->next = NULL; + tobe_removed_bkt = cur_bkt; + break; + } + } + + __hash_rw_writer_unlock(h); + + if (tobe_removed_bkt) { + uint32_t index = tobe_removed_bkt - h->buckets_ext + 1; + rte_ring_mp_enqueue(h->free_ext_bkts, (void *)(uintptr_t)index); + } + return ret; } int32_t @@ -1143,12 +1324,14 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, { uint64_t hits = 0; int32_t i; + int32_t ret; uint32_t prim_hash[RTE_HASH_LOOKUP_BULK_MAX]; uint32_t sec_hash[RTE_HASH_LOOKUP_BULK_MAX]; const struct rte_hash_bucket *primary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; const struct rte_hash_bucket *secondary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; uint32_t prim_hitmask[RTE_HASH_LOOKUP_BULK_MAX] = {0}; uint32_t sec_hitmask[RTE_HASH_LOOKUP_BULK_MAX] = {0}; + struct rte_hash_bucket *cur_bkt, *next_bkt; /* Prefetch first keys */ for (i = 0; i < PREFETCH_OFFSET && i < num_keys; i++) @@ -1266,6 +1449,34 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, continue; } + /* all found, do not need to go through ext bkt */ + if ((hits == ((1ULL << num_keys) - 1)) || !h->ext_table_support) { + if (hit_mask != NULL) + *hit_mask = hits; + __hash_rw_reader_unlock(h); + return; + } + + /* need to check ext buckets for match */ + for (i = 0; i < num_keys; i++) { + if ((hits & (1ULL << i)) != 0) + continue; + next_bkt = secondary_bkt[i]->next; + FOR_EACH_BUCKET(cur_bkt, next_bkt) { + if (data != NULL) + ret = search_one_bucket(h, keys[i], + sec_hash[i], &data[i], cur_bkt); + else + ret = search_one_bucket(h, keys[i], + sec_hash[i], NULL, cur_bkt); + if (ret != -1) { + positions[i] = ret; + hits |= 1ULL << i; + break; + } + } + } + __hash_rw_reader_unlock(h); if (hit_mask != NULL) @@ -1308,10 +1519,13 @@ rte_hash_iterate(const struct rte_hash *h, const void **key, void **data, uint32 RETURN_IF_TRUE(((h == NULL) || (next == NULL)), -EINVAL); - const uint32_t total_entries = h->num_buckets * RTE_HASH_BUCKET_ENTRIES; + const uint32_t total_entries_main = h->num_buckets * + RTE_HASH_BUCKET_ENTRIES; + const uint32_t total_entries = total_entries_main << 1; + /* Out of bounds */ - if (*next >= total_entries) - return -ENOENT; + if (*next >= total_entries_main) + goto extend_table; /* Calculate bucket and index of current iterator */ bucket_idx = *next / RTE_HASH_BUCKET_ENTRIES; @@ -1321,8 +1535,8 @@ rte_hash_iterate(const struct rte_hash *h, const void **key, void **data, uint32 while (h->buckets[bucket_idx].key_idx[idx] == EMPTY_SLOT) { (*next)++; /* End of table */ - if (*next == total_entries) - return -ENOENT; + if (*next == total_entries_main) + goto extend_table; bucket_idx = *next / RTE_HASH_BUCKET_ENTRIES; idx = *next % RTE_HASH_BUCKET_ENTRIES; } @@ -1341,4 +1555,32 @@ rte_hash_iterate(const struct rte_hash *h, const void **key, void **data, uint32 (*next)++; return position - 1; + +extend_table: + /* Out of bounds */ + if (*next >= total_entries || !h->ext_table_support) + return -ENOENT; + + bucket_idx = (*next - total_entries_main) / RTE_HASH_BUCKET_ENTRIES; + idx = (*next - total_entries_main) % RTE_HASH_BUCKET_ENTRIES; + + while (h->buckets_ext[bucket_idx].key_idx[idx] == EMPTY_SLOT) { + (*next)++; + if (*next == total_entries) + return -ENOENT; + bucket_idx = (*next - total_entries_main) / + RTE_HASH_BUCKET_ENTRIES; + idx = (*next - total_entries_main) % RTE_HASH_BUCKET_ENTRIES; + } + /* Get position of entry in key table */ + position = h->buckets_ext[bucket_idx].key_idx[idx]; + next_key = (struct rte_hash_key *) ((char *)h->key_store + + position * h->key_entry_size); + /* Return key and data */ + *key = next_key->key; + *data = next_key->pdata; + + /* Increment iterator */ + (*next)++; + return position - 1; } diff --git a/lib/librte_hash/rte_cuckoo_hash.h b/lib/librte_hash/rte_cuckoo_hash.h index fc0e5c2..e601520 100644 --- a/lib/librte_hash/rte_cuckoo_hash.h +++ b/lib/librte_hash/rte_cuckoo_hash.h @@ -142,6 +142,8 @@ struct rte_hash_bucket { hash_sig_t sig_alt[RTE_HASH_BUCKET_ENTRIES]; uint8_t flag[RTE_HASH_BUCKET_ENTRIES]; + + void *next; } __rte_cache_aligned; /** A hash table structure. */ @@ -166,6 +168,7 @@ struct rte_hash { /**< If multi-writer support is enabled. */ uint8_t readwrite_concur_support; /**< If read-write concurrency support is enabled */ + uint8_t ext_table_support; /**< Enable extendable bucket table */ rte_hash_function hash_func; /**< Function used to calculate hash. */ uint32_t hash_func_init_val; /**< Init value used by hash_func. */ rte_hash_cmp_eq_t rte_hash_custom_cmp_eq; @@ -184,6 +187,8 @@ struct rte_hash { * to the key table. */ rte_rwlock_t *readwrite_lock; /**< Read-write lock thread-safety. */ + struct rte_hash_bucket *buckets_ext; /**< Extra buckets array */ + struct rte_ring *free_ext_bkts; /**< Ring of indexes of free buckets */ } __rte_cache_aligned; struct queue_node { diff --git a/lib/librte_hash/rte_hash.h b/lib/librte_hash/rte_hash.h index 9e7d931..11d8e28 100644 --- a/lib/librte_hash/rte_hash.h +++ b/lib/librte_hash/rte_hash.h @@ -37,6 +37,9 @@ extern "C" { /** Flag to support reader writer concurrency */ #define RTE_HASH_EXTRA_FLAGS_RW_CONCURRENCY 0x04 +/** Flag to indicate the extendabe bucket table feature should be used */ +#define RTE_HASH_EXTRA_FLAGS_EXT_TABLE 0x08 + /** Signature of key that is stored internally. */ typedef uint32_t hash_sig_t; From patchwork Fri Sep 21 17:17:34 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Yipeng1" X-Patchwork-Id: 45153 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 169AB5689; Sat, 22 Sep 2018 02:22:31 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by dpdk.org (Postfix) with ESMTP id 99CDC1DA4 for ; Sat, 22 Sep 2018 02:22:13 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 21 Sep 2018 17:22:11 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,287,1534834800"; d="scan'208";a="76346610" Received: from skx-yipeng.jf.intel.com ([10.54.81.175]) by orsmga006.jf.intel.com with ESMTP; 21 Sep 2018 17:22:10 -0700 From: Yipeng Wang To: bruce.richardson@intel.com Cc: dev@dpdk.org, yipeng1.wang@intel.com, michel@digirati.com.br, honnappa.nagarahalli@arm.com Date: Fri, 21 Sep 2018 10:17:34 -0700 Message-Id: <1537550255-252066-7-git-send-email-yipeng1.wang@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> References: <1536253745-133104-1-git-send-email-yipeng1.wang@intel.com> <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> Subject: [dpdk-dev] [PATCH v2 6/7] test/hash: implement extendable bucket hash test X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" This commit changes the current rte_hash unit test to test the extendable table feature and performance. Signed-off-by: Yipeng Wang --- test/test/test_hash.c | 151 +++++++++++++++++++++++++++++++++++++++++++-- test/test/test_hash_perf.c | 114 +++++++++++++++++++++++++--------- 2 files changed, 230 insertions(+), 35 deletions(-) diff --git a/test/test/test_hash.c b/test/test/test_hash.c index b3db9fd..c97095f 100644 --- a/test/test/test_hash.c +++ b/test/test/test_hash.c @@ -660,6 +660,116 @@ static int test_full_bucket(void) return 0; } +/* + * Similar to the test above (full bucket test), but for extendable buckets. + */ +static int test_extendable_bucket(void) +{ + struct rte_hash_parameters params_pseudo_hash = { + .name = "test5", + .entries = 64, + .key_len = sizeof(struct flow_key), /* 13 */ + .hash_func = pseudo_hash, + .hash_func_init_val = 0, + .socket_id = 0, + .extra_flag = RTE_HASH_EXTRA_FLAGS_EXT_TABLE + }; + struct rte_hash *handle; + int pos[64]; + int expected_pos[64]; + unsigned int i; + struct flow_key rand_keys[64]; + + for (i = 0; i < 64; i++) { + rand_keys[i].port_dst = i; + rand_keys[i].port_src = i+1; + } + + handle = rte_hash_create(¶ms_pseudo_hash); + RETURN_IF_ERROR(handle == NULL, "hash creation failed"); + + /* Fill bucket */ + for (i = 0; i < 64; i++) { + pos[i] = rte_hash_add_key(handle, &rand_keys[i]); + print_key_info("Add", &rand_keys[i], pos[i]); + RETURN_IF_ERROR(pos[i] < 0, + "failed to add key (pos[%u]=%d)", i, pos[i]); + expected_pos[i] = pos[i]; + } + + /* Lookup */ + for (i = 0; i < 64; i++) { + pos[i] = rte_hash_lookup(handle, &rand_keys[i]); + print_key_info("Lkp", &rand_keys[i], pos[i]); + RETURN_IF_ERROR(pos[i] != expected_pos[i], + "failed to find key (pos[%u]=%d)", i, pos[i]); + } + + /* Add - update */ + for (i = 0; i < 64; i++) { + pos[i] = rte_hash_add_key(handle, &rand_keys[i]); + print_key_info("Add", &rand_keys[i], pos[i]); + RETURN_IF_ERROR(pos[i] != expected_pos[i], + "failed to add key (pos[%u]=%d)", i, pos[i]); + } + + /* Lookup */ + for (i = 0; i < 64; i++) { + pos[i] = rte_hash_lookup(handle, &rand_keys[i]); + print_key_info("Lkp", &rand_keys[i], pos[i]); + RETURN_IF_ERROR(pos[i] != expected_pos[i], + "failed to find key (pos[%u]=%d)", i, pos[i]); + } + + /* Delete 1 key, check other keys are still found */ + pos[35] = rte_hash_del_key(handle, &rand_keys[35]); + print_key_info("Del", &rand_keys[35], pos[35]); + RETURN_IF_ERROR(pos[35] != expected_pos[35], + "failed to delete key (pos[1]=%d)", pos[35]); + pos[20] = rte_hash_lookup(handle, &rand_keys[20]); + print_key_info("Lkp", &rand_keys[20], pos[20]); + RETURN_IF_ERROR(pos[20] != expected_pos[20], + "failed lookup after deleting key from same bucket " + "(pos[20]=%d)", pos[20]); + + /* Go back to previous state */ + pos[35] = rte_hash_add_key(handle, &rand_keys[35]); + print_key_info("Add", &rand_keys[35], pos[35]); + expected_pos[35] = pos[35]; + RETURN_IF_ERROR(pos[35] < 0, "failed to add key (pos[1]=%d)", pos[35]); + + /* Delete */ + for (i = 0; i < 64; i++) { + pos[i] = rte_hash_del_key(handle, &rand_keys[i]); + print_key_info("Del", &rand_keys[i], pos[i]); + RETURN_IF_ERROR(pos[i] != expected_pos[i], + "failed to delete key (pos[%u]=%d)", i, pos[i]); + } + + /* Lookup */ + for (i = 0; i < 64; i++) { + pos[i] = rte_hash_lookup(handle, &rand_keys[i]); + print_key_info("Lkp", &rand_keys[i], pos[i]); + RETURN_IF_ERROR(pos[i] != -ENOENT, + "fail: found non-existent key (pos[%u]=%d)", i, pos[i]); + } + + /* Add again */ + for (i = 0; i < 64; i++) { + pos[i] = rte_hash_add_key(handle, &rand_keys[i]); + print_key_info("Add", &rand_keys[i], pos[i]); + RETURN_IF_ERROR(pos[i] < 0, + "failed to add key (pos[%u]=%d)", i, pos[i]); + expected_pos[i] = pos[i]; + } + + rte_hash_free(handle); + + /* Cover the NULL case. */ + rte_hash_free(0); + return 0; +} + /******************************************************************************/ static int fbk_hash_unit_test(void) @@ -1096,7 +1206,7 @@ test_hash_creation_with_good_parameters(void) * Test to see the average table utilization (entries added/max entries) * before hitting a random entry that cannot be added */ -static int test_average_table_utilization(void) +static int test_average_table_utilization(uint32_t ext_table) { struct rte_hash *handle; uint8_t simple_key[MAX_KEYSIZE]; @@ -1107,12 +1217,23 @@ static int test_average_table_utilization(void) printf("\n# Running test to determine average utilization" "\n before adding elements begins to fail\n"); + if (ext_table) + printf("ext table is enabled\n"); + else + printf("ext table is disabled\n"); + printf("Measuring performance, please wait"); fflush(stdout); ut_params.entries = 1 << 16; ut_params.name = "test_average_utilization"; ut_params.hash_func = rte_jhash; + if (ext_table) + ut_params.extra_flag |= RTE_HASH_EXTRA_FLAGS_EXT_TABLE; + else + ut_params.extra_flag &= ~RTE_HASH_EXTRA_FLAGS_EXT_TABLE; + handle = rte_hash_create(&ut_params); + RETURN_IF_ERROR(handle == NULL, "hash creation failed"); for (j = 0; j < ITERATIONS; j++) { @@ -1161,7 +1282,7 @@ static int test_average_table_utilization(void) } #define NUM_ENTRIES 256 -static int test_hash_iteration(void) +static int test_hash_iteration(uint32_t ext_table) { struct rte_hash *handle; unsigned i; @@ -1177,6 +1298,11 @@ static int test_hash_iteration(void) ut_params.name = "test_hash_iteration"; ut_params.hash_func = rte_jhash; ut_params.key_len = 16; + if (ext_table) + ut_params.extra_flag |= RTE_HASH_EXTRA_FLAGS_EXT_TABLE; + else + ut_params.extra_flag &= ~RTE_HASH_EXTRA_FLAGS_EXT_TABLE; + handle = rte_hash_create(&ut_params); RETURN_IF_ERROR(handle == NULL, "hash creation failed"); @@ -1186,8 +1312,13 @@ static int test_hash_iteration(void) for (i = 0; i < ut_params.key_len; i++) keys[added_keys][i] = rte_rand() % 255; ret = rte_hash_add_key_data(handle, keys[added_keys], data[added_keys]); - if (ret < 0) + if (ret < 0) { + if (ext_table) { + printf("Insertion failed for ext table\n"); + goto err; + } break; + } } /* Iterate through the hash table */ @@ -1474,6 +1605,8 @@ test_hash(void) return -1; if (test_full_bucket() < 0) return -1; + if (test_extendable_bucket() < 0) + return -1; if (test_fbk_hash_find_existing() < 0) return -1; @@ -1483,9 +1616,17 @@ test_hash(void) return -1; if (test_hash_creation_with_good_parameters() < 0) return -1; - if (test_average_table_utilization() < 0) + + /* ext table disabled */ + if (test_average_table_utilization(0) < 0) + return -1; + if (test_hash_iteration(0) < 0) + return -1; + + /* ext table enabled */ + if (test_average_table_utilization(1) < 0) return -1; - if (test_hash_iteration() < 0) + if (test_hash_iteration(1) < 0) return -1; run_hash_func_tests(); diff --git a/test/test/test_hash_perf.c b/test/test/test_hash_perf.c index 4d00c20..d169cd0 100644 --- a/test/test/test_hash_perf.c +++ b/test/test/test_hash_perf.c @@ -18,7 +18,8 @@ #include "test.h" #define MAX_ENTRIES (1 << 19) -#define KEYS_TO_ADD (MAX_ENTRIES * 3 / 4) /* 75% table utilization */ +#define KEYS_TO_ADD (MAX_ENTRIES) +#define ADD_PERCENT 0.75 /* 75% table utilization */ #define NUM_LOOKUPS (KEYS_TO_ADD * 5) /* Loop among keys added, several times */ #define BUCKET_SIZE 8 #define NUM_BUCKETS (MAX_ENTRIES / BUCKET_SIZE) @@ -77,7 +78,7 @@ static struct rte_hash_parameters ut_params = { static int create_table(unsigned int with_data, unsigned int table_index, - unsigned int with_locks) + unsigned int with_locks, unsigned int ext) { char name[RTE_HASH_NAMESIZE]; @@ -95,6 +96,9 @@ create_table(unsigned int with_data, unsigned int table_index, else ut_params.extra_flag = 0; + if (ext) + ut_params.extra_flag |= RTE_HASH_EXTRA_FLAGS_EXT_TABLE; + ut_params.name = name; ut_params.key_len = hashtest_key_lens[table_index]; ut_params.socket_id = rte_socket_id(); @@ -116,15 +120,21 @@ create_table(unsigned int with_data, unsigned int table_index, /* Shuffle the keys that have been added, so lookups will be totally random */ static void -shuffle_input_keys(unsigned table_index) +shuffle_input_keys(unsigned int table_index, unsigned int ext) { unsigned i; uint32_t swap_idx; uint8_t temp_key[MAX_KEYSIZE]; hash_sig_t temp_signature; int32_t temp_position; + unsigned int keys_to_add; + + if (!ext) + keys_to_add = KEYS_TO_ADD * ADD_PERCENT; + else + keys_to_add = KEYS_TO_ADD; - for (i = KEYS_TO_ADD - 1; i > 0; i--) { + for (i = keys_to_add - 1; i > 0; i--) { swap_idx = rte_rand() % i; memcpy(temp_key, keys[i], hashtest_key_lens[table_index]); @@ -146,14 +156,20 @@ shuffle_input_keys(unsigned table_index) * ALL can fit in hash table (no errors) */ static int -get_input_keys(unsigned with_pushes, unsigned table_index) +get_input_keys(unsigned int with_pushes, unsigned int table_index, + unsigned int ext) { unsigned i, j; unsigned bucket_idx, incr, success = 1; uint8_t k = 0; int32_t ret; const uint32_t bucket_bitmask = NUM_BUCKETS - 1; + unsigned int keys_to_add; + if (!ext) + keys_to_add = KEYS_TO_ADD * ADD_PERCENT; + else + keys_to_add = KEYS_TO_ADD; /* Reset all arrays */ for (i = 0; i < MAX_ENTRIES; i++) slot_taken[i] = 0; @@ -170,7 +186,7 @@ get_input_keys(unsigned with_pushes, unsigned table_index) * Regardless a key has been added correctly or not (success), * the next one to try will be increased by 1. */ - for (i = 0; i < KEYS_TO_ADD;) { + for (i = 0; i < keys_to_add;) { incr = 0; if (i != 0) { keys[i][0] = ++k; @@ -234,14 +250,20 @@ get_input_keys(unsigned with_pushes, unsigned table_index) } static int -timed_adds(unsigned with_hash, unsigned with_data, unsigned table_index) +timed_adds(unsigned int with_hash, unsigned int with_data, + unsigned int table_index, unsigned int ext) { unsigned i; const uint64_t start_tsc = rte_rdtsc(); void *data; int32_t ret; + unsigned int keys_to_add; + if (!ext) + keys_to_add = KEYS_TO_ADD * ADD_PERCENT; + else + keys_to_add = KEYS_TO_ADD; - for (i = 0; i < KEYS_TO_ADD; i++) { + for (i = 0; i < keys_to_add; i++) { data = (void *) ((uintptr_t) signatures[i]); if (with_hash && with_data) { ret = rte_hash_add_key_with_hash_data(h[table_index], @@ -283,22 +305,31 @@ timed_adds(unsigned with_hash, unsigned with_data, unsigned table_index) const uint64_t end_tsc = rte_rdtsc(); const uint64_t time_taken = end_tsc - start_tsc; - cycles[table_index][ADD][with_hash][with_data] = time_taken/KEYS_TO_ADD; + cycles[table_index][ADD][with_hash][with_data] = time_taken/keys_to_add; return 0; } static int -timed_lookups(unsigned with_hash, unsigned with_data, unsigned table_index) +timed_lookups(unsigned int with_hash, unsigned int with_data, + unsigned int table_index, unsigned int ext) { unsigned i, j; const uint64_t start_tsc = rte_rdtsc(); void *ret_data; void *expected_data; int32_t ret; - - for (i = 0; i < NUM_LOOKUPS/KEYS_TO_ADD; i++) { - for (j = 0; j < KEYS_TO_ADD; j++) { + unsigned int keys_to_add, num_lookups; + + if (!ext) { + keys_to_add = KEYS_TO_ADD * ADD_PERCENT; + num_lookups = NUM_LOOKUPS * ADD_PERCENT; + } else { + keys_to_add = KEYS_TO_ADD; + num_lookups = NUM_LOOKUPS; + } + for (i = 0; i < num_lookups / keys_to_add; i++) { + for (j = 0; j < keys_to_add; j++) { if (with_hash && with_data) { ret = rte_hash_lookup_with_hash_data(h[table_index], (const void *) keys[j], @@ -351,13 +382,14 @@ timed_lookups(unsigned with_hash, unsigned with_data, unsigned table_index) const uint64_t end_tsc = rte_rdtsc(); const uint64_t time_taken = end_tsc - start_tsc; - cycles[table_index][LOOKUP][with_hash][with_data] = time_taken/NUM_LOOKUPS; + cycles[table_index][LOOKUP][with_hash][with_data] = time_taken/num_lookups; return 0; } static int -timed_lookups_multi(unsigned with_data, unsigned table_index) +timed_lookups_multi(unsigned int with_data, unsigned int table_index, + unsigned int ext) { unsigned i, j, k; int32_t positions_burst[BURST_SIZE]; @@ -366,11 +398,20 @@ timed_lookups_multi(unsigned with_data, unsigned table_index) void *ret_data[BURST_SIZE]; uint64_t hit_mask; int ret; + unsigned int keys_to_add, num_lookups; + + if (!ext) { + keys_to_add = KEYS_TO_ADD * ADD_PERCENT; + num_lookups = NUM_LOOKUPS * ADD_PERCENT; + } else { + keys_to_add = KEYS_TO_ADD; + num_lookups = NUM_LOOKUPS; + } const uint64_t start_tsc = rte_rdtsc(); - for (i = 0; i < NUM_LOOKUPS/KEYS_TO_ADD; i++) { - for (j = 0; j < KEYS_TO_ADD/BURST_SIZE; j++) { + for (i = 0; i < num_lookups/keys_to_add; i++) { + for (j = 0; j < keys_to_add/BURST_SIZE; j++) { for (k = 0; k < BURST_SIZE; k++) keys_burst[k] = keys[j * BURST_SIZE + k]; if (with_data) { @@ -418,19 +459,25 @@ timed_lookups_multi(unsigned with_data, unsigned table_index) const uint64_t end_tsc = rte_rdtsc(); const uint64_t time_taken = end_tsc - start_tsc; - cycles[table_index][LOOKUP_MULTI][0][with_data] = time_taken/NUM_LOOKUPS; + cycles[table_index][LOOKUP_MULTI][0][with_data] = time_taken/num_lookups; return 0; } static int -timed_deletes(unsigned with_hash, unsigned with_data, unsigned table_index) +timed_deletes(unsigned int with_hash, unsigned int with_data, + unsigned int table_index, unsigned int ext) { unsigned i; const uint64_t start_tsc = rte_rdtsc(); int32_t ret; + unsigned int keys_to_add; + if (!ext) + keys_to_add = KEYS_TO_ADD * ADD_PERCENT; + else + keys_to_add = KEYS_TO_ADD; - for (i = 0; i < KEYS_TO_ADD; i++) { + for (i = 0; i < keys_to_add; i++) { /* There are no delete functions with data, so just call two functions */ if (with_hash) ret = rte_hash_del_key_with_hash(h[table_index], @@ -450,7 +497,7 @@ timed_deletes(unsigned with_hash, unsigned with_data, unsigned table_index) const uint64_t end_tsc = rte_rdtsc(); const uint64_t time_taken = end_tsc - start_tsc; - cycles[table_index][DELETE][with_hash][with_data] = time_taken/KEYS_TO_ADD; + cycles[table_index][DELETE][with_hash][with_data] = time_taken/keys_to_add; return 0; } @@ -468,7 +515,8 @@ reset_table(unsigned table_index) } static int -run_all_tbl_perf_tests(unsigned int with_pushes, unsigned int with_locks) +run_all_tbl_perf_tests(unsigned int with_pushes, unsigned int with_locks, + unsigned int ext) { unsigned i, j, with_data, with_hash; @@ -477,25 +525,25 @@ run_all_tbl_perf_tests(unsigned int with_pushes, unsigned int with_locks) for (with_data = 0; with_data <= 1; with_data++) { for (i = 0; i < NUM_KEYSIZES; i++) { - if (create_table(with_data, i, with_locks) < 0) + if (create_table(with_data, i, with_locks, ext) < 0) return -1; - if (get_input_keys(with_pushes, i) < 0) + if (get_input_keys(with_pushes, i, ext) < 0) return -1; for (with_hash = 0; with_hash <= 1; with_hash++) { - if (timed_adds(with_hash, with_data, i) < 0) + if (timed_adds(with_hash, with_data, i, ext) < 0) return -1; for (j = 0; j < NUM_SHUFFLES; j++) - shuffle_input_keys(i); + shuffle_input_keys(i, ext); - if (timed_lookups(with_hash, with_data, i) < 0) + if (timed_lookups(with_hash, with_data, i, ext) < 0) return -1; - if (timed_lookups_multi(with_data, i) < 0) + if (timed_lookups_multi(with_data, i, ext) < 0) return -1; - if (timed_deletes(with_hash, with_data, i) < 0) + if (timed_deletes(with_hash, with_data, i, ext) < 0) return -1; /* Print a dot to show progress on operations */ @@ -631,10 +679,16 @@ test_hash_perf(void) printf("\nALL ELEMENTS IN PRIMARY LOCATION\n"); else printf("\nELEMENTS IN PRIMARY OR SECONDARY LOCATION\n"); - if (run_all_tbl_perf_tests(with_pushes, with_locks) < 0) + if (run_all_tbl_perf_tests(with_pushes, with_locks, 0) < 0) return -1; } } + + printf("\n EXTENDABLE BUCKETS PERFORMANCE\n"); + + if (run_all_tbl_perf_tests(1, 0, 1) < 0) + return -1; + if (fbk_hash_perf_test() < 0) return -1; From patchwork Fri Sep 21 17:17:35 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Wang, Yipeng1" X-Patchwork-Id: 45155 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@dpdk.org Delivered-To: patchwork@dpdk.org Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id 17FB65911; Sat, 22 Sep 2018 02:22:42 +0200 (CEST) Received: from mga11.intel.com (mga11.intel.com [192.55.52.93]) by dpdk.org (Postfix) with ESMTP id 35E714CA5 for ; Sat, 22 Sep 2018 02:22:14 +0200 (CEST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga006.jf.intel.com ([10.7.209.51]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 21 Sep 2018 17:22:11 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,287,1534834800"; d="scan'208";a="76346612" Received: from skx-yipeng.jf.intel.com ([10.54.81.175]) by orsmga006.jf.intel.com with ESMTP; 21 Sep 2018 17:22:10 -0700 From: Yipeng Wang To: bruce.richardson@intel.com Cc: dev@dpdk.org, yipeng1.wang@intel.com, michel@digirati.com.br, honnappa.nagarahalli@arm.com Date: Fri, 21 Sep 2018 10:17:35 -0700 Message-Id: <1537550255-252066-8-git-send-email-yipeng1.wang@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> References: <1536253745-133104-1-git-send-email-yipeng1.wang@intel.com> <1537550255-252066-1-git-send-email-yipeng1.wang@intel.com> Subject: [dpdk-dev] [PATCH v2 7/7] hash: use partial-key hashing X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" This commit changes the hashing mechanism to "partial-key hashing" to calculate bucket index and signature of key. This is proposed in Bin Fan, et al's paper "MemC3: Compact and Concurrent MemCache with Dumber Caching and Smarter Hashing". Bascially the idea is to use "xor" to derive alternative bucket from current bucket index and signature. With "partial-key hashing", it reduces the bucket memory requirement from two cache lines to one cache line, which improves the memory efficiency and thus the lookup speed. Signed-off-by: Yipeng Wang --- lib/librte_hash/rte_cuckoo_hash.c | 228 ++++++++++++++++++-------------------- lib/librte_hash/rte_cuckoo_hash.h | 6 +- lib/librte_hash/rte_hash.h | 5 +- 3 files changed, 114 insertions(+), 125 deletions(-) diff --git a/lib/librte_hash/rte_cuckoo_hash.c b/lib/librte_hash/rte_cuckoo_hash.c index 616900b..5108ff0 100644 --- a/lib/librte_hash/rte_cuckoo_hash.c +++ b/lib/librte_hash/rte_cuckoo_hash.c @@ -90,6 +90,27 @@ rte_hash_cmp_eq(const void *key1, const void *key2, const struct rte_hash *h) return cmp_jump_table[h->cmp_jump_table_idx](key1, key2, h->key_len); } +static inline void +get_buckets_index(const struct rte_hash *h, const hash_sig_t hash, + uint32_t *prim_bkt, uint32_t *sec_bkt, uint16_t *sig) +{ + /* + * We use higher 16 bits of hash as the signature value stored in table. + * We use the lower bits for the primary bucket + * location. Then we XOR primary bucket location and the signature + * to get the secondary bucket location. This is same as + * proposed in Bin Fan, et al's paper + * "MemC3: Compact and Concurrent MemCache with Dumber Caching and + * Smarter Hashing". The benefit to use + * XOR is that one could derive the alternative bucket location + * by only using the current bucket location and the signature. + */ + *sig = hash >> 16; + + *prim_bkt = hash & h->bucket_bitmask; + *sec_bkt = (*prim_bkt ^ *sig) & h->bucket_bitmask; +} + struct rte_hash * rte_hash_create(const struct rte_hash_parameters *params) { @@ -327,9 +348,7 @@ rte_hash_create(const struct rte_hash_parameters *params) h->ext_table_support = ext_table_support; #if defined(RTE_ARCH_X86) - if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2)) - h->sig_cmp_fn = RTE_HASH_COMPARE_AVX2; - else if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE2)) + if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_SSE2)) h->sig_cmp_fn = RTE_HASH_COMPARE_SSE; else #endif @@ -416,18 +435,6 @@ rte_hash_hash(const struct rte_hash *h, const void *key) return h->hash_func(key, h->key_len, h->hash_func_init_val); } -/* Calc the secondary hash value from the primary hash value of a given key */ -static inline hash_sig_t -rte_hash_secondary_hash(const hash_sig_t primary_hash) -{ - static const unsigned all_bits_shift = 12; - static const unsigned alt_bits_xor = 0x5bd1e995; - - uint32_t tag = primary_hash >> all_bits_shift; - - return primary_hash ^ ((tag + 1) * alt_bits_xor); -} - int32_t rte_hash_count(const struct rte_hash *h) { @@ -558,14 +565,13 @@ enqueue_slot_back(const struct rte_hash *h, /* Search a key from bucket and update its data */ static inline int32_t search_and_update(const struct rte_hash *h, void *data, const void *key, - struct rte_hash_bucket *bkt, hash_sig_t sig, hash_sig_t alt_hash) + struct rte_hash_bucket *bkt, uint16_t sig) { int i; struct rte_hash_key *k, *keys = h->key_store; for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { - if (bkt->sig_current[i] == sig && - bkt->sig_alt[i] == alt_hash) { + if (bkt->sig_current[i] == sig) { k = (struct rte_hash_key *) ((char *)keys + bkt->key_idx[i] * h->key_entry_size); if (rte_hash_cmp_eq(key, k->key, h) == 0) { @@ -592,7 +598,7 @@ rte_hash_cuckoo_insert_mw(const struct rte_hash *h, struct rte_hash_bucket *prim_bkt, struct rte_hash_bucket *sec_bkt, const struct rte_hash_key *key, void *data, - hash_sig_t sig, hash_sig_t alt_hash, uint32_t new_idx, + uint16_t sig, uint32_t new_idx, int32_t *ret_val) { unsigned int i; @@ -603,7 +609,7 @@ rte_hash_cuckoo_insert_mw(const struct rte_hash *h, /* Check if key was inserted after last check but before this * protected region in case of inserting duplicated keys. */ - ret = search_and_update(h, data, key, prim_bkt, sig, alt_hash); + ret = search_and_update(h, data, key, prim_bkt, sig); if (ret != -1) { __hash_rw_writer_unlock(h); *ret_val = ret; @@ -611,7 +617,7 @@ rte_hash_cuckoo_insert_mw(const struct rte_hash *h, } FOR_EACH_BUCKET(cur_bkt, sec_bkt) { - ret = search_and_update(h, data, key, cur_bkt, alt_hash, sig); + ret = search_and_update(h, data, key, cur_bkt, sig); if (ret != -1) { __hash_rw_writer_unlock(h); *ret_val = ret; @@ -626,7 +632,6 @@ rte_hash_cuckoo_insert_mw(const struct rte_hash *h, /* Check if slot is available */ if (likely(prim_bkt->key_idx[i] == EMPTY_SLOT)) { prim_bkt->sig_current[i] = sig; - prim_bkt->sig_alt[i] = alt_hash; prim_bkt->key_idx[i] = new_idx; break; } @@ -651,7 +656,7 @@ rte_hash_cuckoo_move_insert_mw(const struct rte_hash *h, struct rte_hash_bucket *alt_bkt, const struct rte_hash_key *key, void *data, struct queue_node *leaf, uint32_t leaf_slot, - hash_sig_t sig, hash_sig_t alt_hash, uint32_t new_idx, + uint16_t sig, uint32_t new_idx, int32_t *ret_val) { uint32_t prev_alt_bkt_idx; @@ -672,7 +677,7 @@ rte_hash_cuckoo_move_insert_mw(const struct rte_hash *h, /* Check if key was inserted after last check but before this * protected region. */ - ret = search_and_update(h, data, key, bkt, sig, alt_hash); + ret = search_and_update(h, data, key, bkt, sig); if (ret != -1) { __hash_rw_writer_unlock(h); *ret_val = ret; @@ -680,7 +685,7 @@ rte_hash_cuckoo_move_insert_mw(const struct rte_hash *h, } FOR_EACH_BUCKET(cur_bkt, alt_bkt) { - ret = search_and_update(h, data, key, cur_bkt, alt_hash, sig); + ret = search_and_update(h, data, key, cur_bkt, sig); if (ret != -1) { __hash_rw_writer_unlock(h); *ret_val = ret; @@ -693,8 +698,9 @@ rte_hash_cuckoo_move_insert_mw(const struct rte_hash *h, prev_bkt = prev_node->bkt; prev_slot = curr_node->prev_slot; - prev_alt_bkt_idx = - prev_bkt->sig_alt[prev_slot] & h->bucket_bitmask; + prev_alt_bkt_idx = (prev_node->cur_bkt_idx ^ + prev_bkt->sig_current[prev_slot]) & + h->bucket_bitmask; if (unlikely(&h->buckets[prev_alt_bkt_idx] != curr_bkt)) { @@ -708,10 +714,8 @@ rte_hash_cuckoo_move_insert_mw(const struct rte_hash *h, * Cuckoo insert to move elements back to its * primary bucket if available */ - curr_bkt->sig_alt[curr_slot] = - prev_bkt->sig_current[prev_slot]; curr_bkt->sig_current[curr_slot] = - prev_bkt->sig_alt[prev_slot]; + prev_bkt->sig_current[prev_slot]; curr_bkt->key_idx[curr_slot] = prev_bkt->key_idx[prev_slot]; @@ -721,7 +725,6 @@ rte_hash_cuckoo_move_insert_mw(const struct rte_hash *h, } curr_bkt->sig_current[curr_slot] = sig; - curr_bkt->sig_alt[curr_slot] = alt_hash; curr_bkt->key_idx[curr_slot] = new_idx; __hash_rw_writer_unlock(h); @@ -739,39 +742,44 @@ rte_hash_cuckoo_make_space_mw(const struct rte_hash *h, struct rte_hash_bucket *bkt, struct rte_hash_bucket *sec_bkt, const struct rte_hash_key *key, void *data, - hash_sig_t sig, hash_sig_t alt_hash, + uint16_t sig, uint32_t bucket_idx, uint32_t new_idx, int32_t *ret_val) { unsigned int i; struct queue_node queue[RTE_HASH_BFS_QUEUE_MAX_LEN]; struct queue_node *tail, *head; struct rte_hash_bucket *curr_bkt, *alt_bkt; + uint32_t cur_idx, alt_idx; tail = queue; head = queue + 1; tail->bkt = bkt; tail->prev = NULL; tail->prev_slot = -1; + tail->cur_bkt_idx = bucket_idx; /* Cuckoo bfs Search */ while (likely(tail != head && head < queue + RTE_HASH_BFS_QUEUE_MAX_LEN - RTE_HASH_BUCKET_ENTRIES)) { curr_bkt = tail->bkt; + cur_idx = tail->cur_bkt_idx; for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { if (curr_bkt->key_idx[i] == EMPTY_SLOT) { int32_t ret = rte_hash_cuckoo_move_insert_mw(h, bkt, sec_bkt, key, data, - tail, i, sig, alt_hash, + tail, i, sig, new_idx, ret_val); if (likely(ret != -1)) return ret; } /* Enqueue new node and keep prev node info */ - alt_bkt = &(h->buckets[curr_bkt->sig_alt[i] - & h->bucket_bitmask]); + alt_idx = (curr_bkt->sig_current[i] ^ cur_idx) & + h->bucket_bitmask; + alt_bkt = &(h->buckets[alt_idx]); head->bkt = alt_bkt; + head->cur_bkt_idx = alt_idx; head->prev = tail; head->prev_slot = i; head++; @@ -786,7 +794,7 @@ static inline int32_t __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, hash_sig_t sig, void *data) { - hash_sig_t alt_hash; + uint16_t short_sig; uint32_t prim_bucket_idx, sec_bucket_idx; struct rte_hash_bucket *prim_bkt, *sec_bkt, *cur_bkt; struct rte_hash_key *new_k, *keys = h->key_store; @@ -801,18 +809,15 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, int32_t ret_val; struct rte_hash_bucket *last; - prim_bucket_idx = sig & h->bucket_bitmask; + get_buckets_index(h, sig, &prim_bucket_idx, &sec_bucket_idx, &short_sig); prim_bkt = &h->buckets[prim_bucket_idx]; - rte_prefetch0(prim_bkt); - - alt_hash = rte_hash_secondary_hash(sig); - sec_bucket_idx = alt_hash & h->bucket_bitmask; sec_bkt = &h->buckets[sec_bucket_idx]; + rte_prefetch0(prim_bkt); rte_prefetch0(sec_bkt); /* Check if key is already inserted in primary location */ __hash_rw_writer_lock(h); - ret = search_and_update(h, data, key, prim_bkt, sig, alt_hash); + ret = search_and_update(h, data, key, prim_bkt, short_sig); if (ret != -1) { __hash_rw_writer_unlock(h); return ret; @@ -820,12 +825,13 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, /* Check if key is already inserted in secondary location */ FOR_EACH_BUCKET(cur_bkt, sec_bkt) { - ret = search_and_update(h, data, key, cur_bkt, alt_hash, sig); + ret = search_and_update(h, data, key, cur_bkt, short_sig); if (ret != -1) { __hash_rw_writer_unlock(h); return ret; } } + __hash_rw_writer_unlock(h); /* Did not find a match, so get a new slot for storing the new key */ @@ -863,7 +869,7 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, /* Find an empty slot and insert */ ret = rte_hash_cuckoo_insert_mw(h, prim_bkt, sec_bkt, key, data, - sig, alt_hash, new_idx, &ret_val); + short_sig, new_idx, &ret_val); if (ret == 0) return new_idx - 1; else if (ret == 1) { @@ -873,7 +879,7 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, /* Primary bucket full, need to make space for new entry */ ret = rte_hash_cuckoo_make_space_mw(h, prim_bkt, sec_bkt, key, data, - sig, alt_hash, new_idx, &ret_val); + short_sig, prim_bucket_idx, new_idx, &ret_val); if (ret == 0) return new_idx - 1; else if (ret == 1) { @@ -883,7 +889,7 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, /* Also search secondary bucket to get better occupancy */ ret = rte_hash_cuckoo_make_space_mw(h, sec_bkt, prim_bkt, key, data, - alt_hash, sig, new_idx, &ret_val); + short_sig, sec_bucket_idx, new_idx, &ret_val); if (ret == 0) return new_idx - 1; @@ -903,14 +909,14 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, */ __hash_rw_writer_lock(h); /* We check for duplicates again since could be inserted before the lock */ - ret = search_and_update(h, data, key, prim_bkt, sig, alt_hash); + ret = search_and_update(h, data, key, prim_bkt, short_sig); if (ret != -1) { enqueue_slot_back(h, cached_free_slots, slot_id); goto failure; } FOR_EACH_BUCKET(cur_bkt, sec_bkt) { - ret = search_and_update(h, data, key, cur_bkt, alt_hash, sig); + ret = search_and_update(h, data, key, cur_bkt, short_sig); if (ret != -1) { enqueue_slot_back(h, cached_free_slots, slot_id); goto failure; @@ -923,8 +929,7 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { /* Check if slot is available */ if (likely(cur_bkt->key_idx[i] == EMPTY_SLOT)) { - cur_bkt->sig_current[i] = alt_hash; - cur_bkt->sig_alt[i] = sig; + cur_bkt->sig_current[i] = short_sig; cur_bkt->key_idx[i] = new_idx; __hash_rw_writer_unlock(h); return new_idx - 1; @@ -942,8 +947,7 @@ __rte_hash_add_key_with_hash(const struct rte_hash *h, const void *key, bkt_id = (uint32_t)((uintptr_t)ext_bkt_id) - 1; /* Use the first location of the new bucket */ - (h->buckets_ext[bkt_id]).sig_current[0] = alt_hash; - (h->buckets_ext[bkt_id]).sig_alt[0] = sig; + (h->buckets_ext[bkt_id]).sig_current[0] = short_sig; (h->buckets_ext[bkt_id]).key_idx[0] = new_idx; /* Link the new bucket to sec bucket linked list */ last = rte_hash_get_last_bkt(sec_bkt); @@ -1002,7 +1006,7 @@ rte_hash_add_key_data(const struct rte_hash *h, const void *key, void *data) /* Search one bucket to find the match key */ static inline int32_t -search_one_bucket(const struct rte_hash *h, const void *key, hash_sig_t sig, +search_one_bucket(const struct rte_hash *h, const void *key, uint16_t sig, void **data, const struct rte_hash_bucket *bkt) { int i; @@ -1031,30 +1035,28 @@ static inline int32_t __rte_hash_lookup_with_hash(const struct rte_hash *h, const void *key, hash_sig_t sig, void **data) { - uint32_t bucket_idx; - hash_sig_t alt_hash; + uint32_t prim_bucket_idx, sec_bucket_idx; struct rte_hash_bucket *bkt, *cur_bkt; int ret; + uint16_t short_sig; - bucket_idx = sig & h->bucket_bitmask; - bkt = &h->buckets[bucket_idx]; + get_buckets_index(h, sig, &prim_bucket_idx, &sec_bucket_idx, &short_sig); + bkt = &h->buckets[prim_bucket_idx]; __hash_rw_reader_lock(h); /* Check if key is in primary location */ - ret = search_one_bucket(h, key, sig, data, bkt); + ret = search_one_bucket(h, key, short_sig, data, bkt); if (ret != -1) { __hash_rw_reader_unlock(h); return ret; } /* Calculate secondary hash */ - alt_hash = rte_hash_secondary_hash(sig); - bucket_idx = alt_hash & h->bucket_bitmask; - bkt = &h->buckets[bucket_idx]; + bkt = &h->buckets[sec_bucket_idx]; /* Check if key is in secondary location */ FOR_EACH_BUCKET(cur_bkt, bkt) { - ret = search_one_bucket(h, key, alt_hash, data, cur_bkt); + ret = search_one_bucket(h, key, short_sig, data, cur_bkt); if (ret != -1) { __hash_rw_reader_unlock(h); return ret; @@ -1101,7 +1103,6 @@ remove_entry(const struct rte_hash *h, struct rte_hash_bucket *bkt, unsigned i) struct lcore_cache *cached_free_slots; bkt->sig_current[i] = NULL_SIGNATURE; - bkt->sig_alt[i] = NULL_SIGNATURE; if (h->multi_writer_support) { lcore_id = rte_lcore_id(); cached_free_slots = &h->local_free_slots[lcore_id]; @@ -1126,7 +1127,7 @@ remove_entry(const struct rte_hash *h, struct rte_hash_bucket *bkt, unsigned i) /* Search one bucket and remove the matched key */ static inline int32_t search_and_remove(const struct rte_hash *h, const void *key, - struct rte_hash_bucket *bkt, hash_sig_t sig) + struct rte_hash_bucket *bkt, uint16_t sig) { struct rte_hash_key *k, *keys = h->key_store; unsigned int i; @@ -1158,31 +1159,29 @@ static inline int32_t __rte_hash_del_key_with_hash(const struct rte_hash *h, const void *key, hash_sig_t sig) { - uint32_t bucket_idx; - hash_sig_t alt_hash; + uint32_t prim_bucket_idx, sec_bucket_idx; struct rte_hash_bucket *prim_bkt, *sec_bkt; struct rte_hash_bucket *cur_bkt, *prev_bkt, *next_bkt; int32_t ret, i; struct rte_hash_bucket *tobe_removed_bkt = NULL; + uint16_t short_sig; - bucket_idx = sig & h->bucket_bitmask; - prim_bkt = &h->buckets[bucket_idx]; + get_buckets_index(h, sig, &prim_bucket_idx, &sec_bucket_idx, &short_sig); + prim_bkt = &h->buckets[prim_bucket_idx]; __hash_rw_writer_lock(h); /* look for key in primary bucket */ - ret = search_and_remove(h, key, prim_bkt, sig); + ret = search_and_remove(h, key, prim_bkt, short_sig); if (ret != -1) { __hash_rw_writer_unlock(h); return ret; } /* Calculate secondary hash */ - alt_hash = rte_hash_secondary_hash(sig); - bucket_idx = alt_hash & h->bucket_bitmask; - sec_bkt = &h->buckets[bucket_idx]; + sec_bkt = &h->buckets[sec_bucket_idx]; /* look for key in secondary bucket */ - ret = search_and_remove(h, key, sec_bkt, alt_hash); + ret = search_and_remove(h, key, sec_bkt, short_sig); if (ret != -1) { __hash_rw_writer_unlock(h); return ret; @@ -1192,7 +1191,7 @@ __rte_hash_del_key_with_hash(const struct rte_hash *h, const void *key, if (h->ext_table_support) { next_bkt = sec_bkt->next; FOR_EACH_BUCKET(cur_bkt, next_bkt) { - ret = search_and_remove(h, key, cur_bkt, alt_hash); + ret = search_and_remove(h, key, cur_bkt, short_sig); if (ret != -1) goto return_bkt; } @@ -1265,55 +1264,35 @@ static inline void compare_signatures(uint32_t *prim_hash_matches, uint32_t *sec_hash_matches, const struct rte_hash_bucket *prim_bkt, const struct rte_hash_bucket *sec_bkt, - hash_sig_t prim_hash, hash_sig_t sec_hash, + uint16_t sig, enum rte_hash_sig_compare_function sig_cmp_fn) { unsigned int i; + /* For match mask the first bit of every two bits indicates the match */ switch (sig_cmp_fn) { -#ifdef RTE_MACHINE_CPUFLAG_AVX2 - case RTE_HASH_COMPARE_AVX2: - *prim_hash_matches = _mm256_movemask_ps((__m256)_mm256_cmpeq_epi32( - _mm256_load_si256( - (__m256i const *)prim_bkt->sig_current), - _mm256_set1_epi32(prim_hash))); - *sec_hash_matches = _mm256_movemask_ps((__m256)_mm256_cmpeq_epi32( - _mm256_load_si256( - (__m256i const *)sec_bkt->sig_current), - _mm256_set1_epi32(sec_hash))); - break; -#endif #ifdef RTE_MACHINE_CPUFLAG_SSE2 case RTE_HASH_COMPARE_SSE: - /* Compare the first 4 signatures in the bucket */ - *prim_hash_matches = _mm_movemask_ps((__m128)_mm_cmpeq_epi16( + /* Compare all signatures in the bucket */ + *prim_hash_matches = _mm_movemask_epi8(_mm_cmpeq_epi16( _mm_load_si128( (__m128i const *)prim_bkt->sig_current), - _mm_set1_epi32(prim_hash))); - *prim_hash_matches |= (_mm_movemask_ps((__m128)_mm_cmpeq_epi16( - _mm_load_si128( - (__m128i const *)&prim_bkt->sig_current[4]), - _mm_set1_epi32(prim_hash)))) << 4; - /* Compare the first 4 signatures in the bucket */ - *sec_hash_matches = _mm_movemask_ps((__m128)_mm_cmpeq_epi16( + _mm_set1_epi16(sig))); + /* Compare all signatures in the bucket */ + *sec_hash_matches = _mm_movemask_epi8(_mm_cmpeq_epi16( _mm_load_si128( (__m128i const *)sec_bkt->sig_current), - _mm_set1_epi32(sec_hash))); - *sec_hash_matches |= (_mm_movemask_ps((__m128)_mm_cmpeq_epi16( - _mm_load_si128( - (__m128i const *)&sec_bkt->sig_current[4]), - _mm_set1_epi32(sec_hash)))) << 4; + _mm_set1_epi16(sig))); break; #endif default: for (i = 0; i < RTE_HASH_BUCKET_ENTRIES; i++) { *prim_hash_matches |= - ((prim_hash == prim_bkt->sig_current[i]) << i); + ((sig == prim_bkt->sig_current[i]) << (i << 1)); *sec_hash_matches |= - ((sec_hash == sec_bkt->sig_current[i]) << i); + ((sig == sec_bkt->sig_current[i]) << (i << 1)); } } - } #define PREFETCH_OFFSET 4 @@ -1326,7 +1305,9 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, int32_t i; int32_t ret; uint32_t prim_hash[RTE_HASH_LOOKUP_BULK_MAX]; - uint32_t sec_hash[RTE_HASH_LOOKUP_BULK_MAX]; + uint32_t prim_index[RTE_HASH_LOOKUP_BULK_MAX]; + uint32_t sec_index[RTE_HASH_LOOKUP_BULK_MAX]; + uint16_t sig[RTE_HASH_LOOKUP_BULK_MAX]; const struct rte_hash_bucket *primary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; const struct rte_hash_bucket *secondary_bkt[RTE_HASH_LOOKUP_BULK_MAX]; uint32_t prim_hitmask[RTE_HASH_LOOKUP_BULK_MAX] = {0}; @@ -1345,10 +1326,11 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, rte_prefetch0(keys[i + PREFETCH_OFFSET]); prim_hash[i] = rte_hash_hash(h, keys[i]); - sec_hash[i] = rte_hash_secondary_hash(prim_hash[i]); + get_buckets_index(h, prim_hash[i], + &prim_index[i], &sec_index[i], &sig[i]); - primary_bkt[i] = &h->buckets[prim_hash[i] & h->bucket_bitmask]; - secondary_bkt[i] = &h->buckets[sec_hash[i] & h->bucket_bitmask]; + primary_bkt[i] = &h->buckets[prim_index[i]]; + secondary_bkt[i] = &h->buckets[sec_index[i]]; rte_prefetch0(primary_bkt[i]); rte_prefetch0(secondary_bkt[i]); @@ -1357,10 +1339,12 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, /* Calculate and prefetch rest of the buckets */ for (; i < num_keys; i++) { prim_hash[i] = rte_hash_hash(h, keys[i]); - sec_hash[i] = rte_hash_secondary_hash(prim_hash[i]); - primary_bkt[i] = &h->buckets[prim_hash[i] & h->bucket_bitmask]; - secondary_bkt[i] = &h->buckets[sec_hash[i] & h->bucket_bitmask]; + get_buckets_index(h, prim_hash[i], + &prim_index[i], &sec_index[i], &sig[i]); + + primary_bkt[i] = &h->buckets[prim_index[i]]; + secondary_bkt[i] = &h->buckets[sec_index[i]]; rte_prefetch0(primary_bkt[i]); rte_prefetch0(secondary_bkt[i]); @@ -1371,10 +1355,11 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, for (i = 0; i < num_keys; i++) { compare_signatures(&prim_hitmask[i], &sec_hitmask[i], primary_bkt[i], secondary_bkt[i], - prim_hash[i], sec_hash[i], h->sig_cmp_fn); + sig[i], h->sig_cmp_fn); if (prim_hitmask[i]) { - uint32_t first_hit = __builtin_ctzl(prim_hitmask[i]); + uint32_t first_hit = + __builtin_ctzl(prim_hitmask[i]) >> 1; uint32_t key_idx = primary_bkt[i]->key_idx[first_hit]; const struct rte_hash_key *key_slot = (const struct rte_hash_key *)( @@ -1385,7 +1370,8 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, } if (sec_hitmask[i]) { - uint32_t first_hit = __builtin_ctzl(sec_hitmask[i]); + uint32_t first_hit = + __builtin_ctzl(sec_hitmask[i]) >> 1; uint32_t key_idx = secondary_bkt[i]->key_idx[first_hit]; const struct rte_hash_key *key_slot = (const struct rte_hash_key *)( @@ -1399,7 +1385,8 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, for (i = 0; i < num_keys; i++) { positions[i] = -ENOENT; while (prim_hitmask[i]) { - uint32_t hit_index = __builtin_ctzl(prim_hitmask[i]); + uint32_t hit_index = + __builtin_ctzl(prim_hitmask[i]) >> 1; uint32_t key_idx = primary_bkt[i]->key_idx[hit_index]; const struct rte_hash_key *key_slot = @@ -1418,11 +1405,12 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, positions[i] = key_idx - 1; goto next_key; } - prim_hitmask[i] &= ~(1 << (hit_index)); + prim_hitmask[i] &= ~(3ULL << (hit_index << 1)); } while (sec_hitmask[i]) { - uint32_t hit_index = __builtin_ctzl(sec_hitmask[i]); + uint32_t hit_index = + __builtin_ctzl(sec_hitmask[i]) >> 1; uint32_t key_idx = secondary_bkt[i]->key_idx[hit_index]; const struct rte_hash_key *key_slot = @@ -1442,7 +1430,7 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, positions[i] = key_idx - 1; goto next_key; } - sec_hitmask[i] &= ~(1 << (hit_index)); + sec_hitmask[i] &= ~(3ULL << (hit_index << 1)); } next_key: @@ -1465,10 +1453,10 @@ __rte_hash_lookup_bulk(const struct rte_hash *h, const void **keys, FOR_EACH_BUCKET(cur_bkt, next_bkt) { if (data != NULL) ret = search_one_bucket(h, keys[i], - sec_hash[i], &data[i], cur_bkt); + sig[i], &data[i], cur_bkt); else ret = search_one_bucket(h, keys[i], - sec_hash[i], NULL, cur_bkt); + sig[i], NULL, cur_bkt); if (ret != -1) { positions[i] = ret; hits |= 1ULL << i; diff --git a/lib/librte_hash/rte_cuckoo_hash.h b/lib/librte_hash/rte_cuckoo_hash.h index e601520..7753cd8 100644 --- a/lib/librte_hash/rte_cuckoo_hash.h +++ b/lib/librte_hash/rte_cuckoo_hash.h @@ -129,18 +129,15 @@ struct rte_hash_key { enum rte_hash_sig_compare_function { RTE_HASH_COMPARE_SCALAR = 0, RTE_HASH_COMPARE_SSE, - RTE_HASH_COMPARE_AVX2, RTE_HASH_COMPARE_NUM }; /** Bucket structure */ struct rte_hash_bucket { - hash_sig_t sig_current[RTE_HASH_BUCKET_ENTRIES]; + uint16_t sig_current[RTE_HASH_BUCKET_ENTRIES]; uint32_t key_idx[RTE_HASH_BUCKET_ENTRIES]; - hash_sig_t sig_alt[RTE_HASH_BUCKET_ENTRIES]; - uint8_t flag[RTE_HASH_BUCKET_ENTRIES]; void *next; @@ -193,6 +190,7 @@ struct rte_hash { struct queue_node { struct rte_hash_bucket *bkt; /* Current bucket on the bfs search */ + uint32_t cur_bkt_idx; struct queue_node *prev; /* Parent(bucket) in search path */ int prev_slot; /* Parent(slot) in search path */ diff --git a/lib/librte_hash/rte_hash.h b/lib/librte_hash/rte_hash.h index 11d8e28..0bd7696 100644 --- a/lib/librte_hash/rte_hash.h +++ b/lib/librte_hash/rte_hash.h @@ -40,7 +40,10 @@ extern "C" { /** Flag to indicate the extendabe bucket table feature should be used */ #define RTE_HASH_EXTRA_FLAGS_EXT_TABLE 0x08 -/** Signature of key that is stored internally. */ +/** + * A hash value that is used to generate signature stored in table and the + * location the signature is stored. + */ typedef uint32_t hash_sig_t; /** Type of function that can be used for calculating the hash value. */