diff options
author | Xavier Simonart <xavier.simonart@intel.com> | 2019-12-29 18:41:50 +0100 |
---|---|---|
committer | Xavier Simonart <xavier.simonart@intel.com> | 2020-01-29 12:12:27 +0100 |
commit | 7ef348cff20a6d35fe36bb551e5b1aaa69eded1a (patch) | |
tree | eb3570fbdabd29f6af9e0d695ea8fbd7c8c8f5a5 /VNFs/DPPD-PROX | |
parent | a984177b93bd59fec43da19193b8ee5204d92e00 (diff) |
Add Latency Distribution display
- Latency Distribution (i.e. histograms) has been added when latency is enabled.
- Histograms are now compiled by default.
- A bug has also been fixed, which was causing strange behavior in the histograms
(due to bucket size being overwritten).
- The default bucket size has been changed to "11" which means that the size of each bucket
is now (1 cycle << 11) = 2048 cycles = 1 microsecond at 2GHz. It was (1 << (10 - 1)).
As there are 128 latency buckets, it is not possible to show all of them on the display.
Hence the latency buckets are displayed based on on the assumption that the minimum latency is usually relatively
stable and that the maximum latency change more often.
- The first empty buckets are not shown (i.e. buckets empty for all tasks)
- If more than 15 buckets are non empty, then the buckets are combined, so that a maximum of
15 (bigger) buckets are shown
- If less than 15 buckets are non empty, the following (empty) buckets are shown
(this avoid seeing every x seconds some columns being added and removed).
Change-Id: I27fe6ac0e513a5558e42ff2e74255c55ba79516d
Signed-off-by: Xavier Simonart <xavier.simonart@intel.com>
Diffstat (limited to 'VNFs/DPPD-PROX')
-rw-r--r-- | VNFs/DPPD-PROX/Makefile | 6 | ||||
-rw-r--r-- | VNFs/DPPD-PROX/cmd_parser.c | 4 | ||||
-rw-r--r-- | VNFs/DPPD-PROX/display.c | 11 | ||||
-rw-r--r-- | VNFs/DPPD-PROX/display.h | 6 | ||||
-rw-r--r-- | VNFs/DPPD-PROX/display_latency_distr.c | 193 | ||||
-rw-r--r-- | VNFs/DPPD-PROX/display_latency_distr.h | 23 | ||||
-rw-r--r-- | VNFs/DPPD-PROX/handle_lat.c | 17 | ||||
-rw-r--r-- | VNFs/DPPD-PROX/handle_lat.h | 7 | ||||
-rw-r--r-- | VNFs/DPPD-PROX/stats_latency.c | 24 | ||||
-rw-r--r-- | VNFs/DPPD-PROX/stats_latency.h | 5 |
10 files changed, 278 insertions, 18 deletions
diff --git a/VNFs/DPPD-PROX/Makefile b/VNFs/DPPD-PROX/Makefile index bc11f5d8..f8bde421 100644 --- a/VNFs/DPPD-PROX/Makefile +++ b/VNFs/DPPD-PROX/Makefile @@ -1,5 +1,5 @@ ## -## Copyright (c) 2010-2017 Intel Corporation +## Copyright (c) 2010-2019 Intel Corporation ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## you may not use this file except in compliance with the License. @@ -133,7 +133,7 @@ CFLAGS += -DPROX_PREFETCH_OFFSET=2 #CFLAGS += -DASSERT #CFLAGS += -DENABLE_EXTRA_USER_STATISTICS CFLAGS += -DLATENCY_PER_PACKET -CFLAGS += -DLATENCY_DETAILS +CFLAGS += -DLATENCY_HISTOGRAM CFLAGS += -DGRE_TP CFLAGS += -std=gnu99 CFLAGS += -D_GNU_SOURCE # for PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP @@ -204,7 +204,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PIPELINE) += thread_pipeline.c SRCS-y += prox_args.c prox_cfg.c prox_cksum.c prox_port_cfg.c SRCS-y += cfgfile.c clock.c commands.c cqm.c msr.c defaults.c -SRCS-y += display.c display_latency.c display_mempools.c +SRCS-y += display.c display_latency.c display_latency_distr.c display_mempools.c SRCS-y += display_ports.c display_rings.c display_priority.c display_pkt_len.c display_l4gen.c display_tasks.c display_irq.c SRCS-y += log.c hash_utils.c main.c parse_utils.c file_utils.c SRCS-y += run.c input_conn.c input_curses.c diff --git a/VNFs/DPPD-PROX/cmd_parser.c b/VNFs/DPPD-PROX/cmd_parser.c index a8fe3a0a..3e71c569 100644 --- a/VNFs/DPPD-PROX/cmd_parser.c +++ b/VNFs/DPPD-PROX/cmd_parser.c @@ -1,5 +1,5 @@ /* -// Copyright (c) 2010-2017 Intel Corporation +// Copyright (c) 2010-2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -1910,7 +1910,7 @@ static void task_lat_show_latency_histogram(uint8_t lcore_id, uint8_t task_id, s plog_info("Bucket [%zu]: %"PRIu64"\n", i, buckets[i]); } #else - plog_info("LATENCY_DETAILS disabled\n"); + plog_info("LATENCY_HISTOGRAM disabled\n"); #endif } diff --git a/VNFs/DPPD-PROX/display.c b/VNFs/DPPD-PROX/display.c index d7421e85..e1b8d8d2 100644 --- a/VNFs/DPPD-PROX/display.c +++ b/VNFs/DPPD-PROX/display.c @@ -1,5 +1,5 @@ /* -// Copyright (c) 2010-2017 Intel Corporation +// Copyright (c) 2010-2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ #include "display_ports.h" #include "display_priority.h" #include "display_irq.h" +#include "display_latency_distr.h" #include "display_rings.h" #include "display_pkt_len.h" #include "display_l4gen.h" @@ -292,6 +293,9 @@ static void display_init_screens(void) display_add_screen(display_ports()); display_add_screen(display_mempools()); display_add_screen(display_latency()); +#ifdef LATENCY_HISTOGRAM + display_add_screen(display_latency_distr()); +#endif display_add_screen(display_rings()); display_add_screen(display_l4gen()); display_add_screen(display_pkt_len()); @@ -917,6 +921,11 @@ void display_refresh(void) stats_display_layout(1); } +void display_renew(void) +{ + stats_display_layout(0); +} + void display_stats(void) { display_lock(); diff --git a/VNFs/DPPD-PROX/display.h b/VNFs/DPPD-PROX/display.h index 4b517546..4c9f9ba7 100644 --- a/VNFs/DPPD-PROX/display.h +++ b/VNFs/DPPD-PROX/display.h @@ -1,5 +1,5 @@ /* -// Copyright (c) 2010-2017 Intel Corporation +// Copyright (c) 2010-2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ #ifndef _DISPLAY_H_ #define _DISPLAY_H_ +#define PROX_MAX_COLS 32 #include <inttypes.h> #include <stdarg.h> #include <stdio.h> @@ -33,7 +34,7 @@ struct display_column { }; struct display_table { - struct display_column cols[16]; + struct display_column cols[PROX_MAX_COLS]; char title[32]; int n_cols; int offset; @@ -86,6 +87,7 @@ void display_init(void); void display_end(void); void display_stats(void); void display_refresh(void); +void display_renew(void); void display_print(const char *str); void display_cmd(const char *cmd, int cmd_len, int cursor_pos); void display_screen(unsigned screen_id); diff --git a/VNFs/DPPD-PROX/display_latency_distr.c b/VNFs/DPPD-PROX/display_latency_distr.c new file mode 100644 index 00000000..9808a702 --- /dev/null +++ b/VNFs/DPPD-PROX/display_latency_distr.c @@ -0,0 +1,193 @@ +/* +// Copyright (c) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#include <math.h> +#include "handle_lat.h" +#include "display_latency_distr.h" +#include "stats_latency.h" +#include "display.h" +#include "lconf.h" + +static struct display_page display_page_latency_distr; +static struct display_column *stats_latency_distr[LAT_BUCKET_COUNT]; +static struct display_column *stats_max; +static struct display_column *core_col; +static struct display_column *name_col; +static uint32_t global_min_bucket_id = 0, global_max_bucket_id = LAT_BUCKET_COUNT - 1; +static const uint16_t global_nb_buckets_displayed = 15; +static uint32_t group_size = 9; //LAT_BUCKET_COUNT / global_nb_buckets_displayed; + +#define UNIT_INT(i) (((i) * bucket_unit_nsec)/1000) +#define UNIT_FRACT(i) ((((i) * bucket_unit_nsec) % 1000) / 100) + +static void display_latency_distr_draw_frame(struct screen_state *state) +{ + uint32_t n_tasks = stats_get_n_latency(); + struct lcore_cfg *lconf = NULL; + struct task_args *targ; + char name[32]; + char *ptr; + + display_page_init(&display_page_latency_distr); + + struct display_table *core_name = display_page_add_table(&display_page_latency_distr); + + display_table_init(core_name, "Core/task"); + core_col = display_table_add_col(core_name); + name_col = display_table_add_col(core_name); + display_column_init(core_col, "Nb", 4); + display_column_init(name_col, "Name", 5); + + uint32_t bucket_size = stats_get_latency_bucket_size(); + struct display_table *stats = display_page_add_table(&display_page_latency_distr); + uint32_t bucket_unit_nsec = 1000000000 / (rte_get_tsc_hz() >> bucket_size); + if (state->toggle == 0) { + display_table_init(stats, "Statistics per second"); + } else { + display_table_init(stats, "Total statistics"); + } + char title[64]; + stats_max = display_table_add_col(stats); + snprintf(title, sizeof(title), " MAXIMUM(mic)"); + display_column_init(stats_max, title, 11); + plog_info("Bucket unit is %d nsec, bucket size is %d, freq is %ld\n", bucket_unit_nsec, bucket_size, rte_get_tsc_hz()); + + uint32_t i = global_min_bucket_id, first = i, k = 0; + while ((i < LAT_BUCKET_COUNT) && (i <= global_max_bucket_id)) { + stats_latency_distr[k] = display_table_add_col(stats); + if (i < LAT_BUCKET_COUNT - group_size) { + snprintf(title, sizeof(title), "%d.%01d-%d.%01d", UNIT_INT(i), UNIT_FRACT(i), UNIT_INT(i + group_size), UNIT_FRACT(i + group_size)); + } else { + snprintf(title, sizeof(title), "> %d.%01d", UNIT_INT(i), UNIT_FRACT(i)); + } + display_column_init(stats_latency_distr[k++], title, 9); + i += group_size; + } + display_page_draw_frame(&display_page_latency_distr, n_tasks); + + uint32_t count = 0; + lconf = NULL; + while (core_targ_next(&lconf, &targ, 0) == 0) { + if (strcmp(targ->task_init->mode_str, "lat") == 0) { + display_column_print_core_task(core_col, count, lconf, targ); + if (targ->id == 0) + display_column_print(name_col, count, "%s", lconf->name); + count++; + } + } +} + +static void display_latency_distr_draw_stats(struct screen_state *state) +{ + const uint32_t n_latency = stats_get_n_latency(); + uint64_t *bucket; + uint32_t bucket_id = 0, min_bucket_id = LAT_BUCKET_COUNT - 1, max_bucket_id = 0; + struct time_unit tu; + + for (uint32_t count = 0; count < n_latency; ++count) { + if (state->toggle == 0) + tu = stats_latency_get(count)->max.time; + else + tu = stats_latency_tot_get(count)->max.time; + display_column_print(stats_max, count, "%9lu.%03lu", tu.sec * 1000000 + tu.nsec / 1000, tu.nsec % 1000); + } + + // Calculate min_bucket_id: id of 1st bucket with data for any tasks + // Calculate max_bucket_id: id of last bucket with data for any tasks + for (uint i = 0; i < LAT_BUCKET_COUNT; ++i) { + for (uint32_t count = 0; count < n_latency; ++count) { + if (state->toggle == 0) + bucket = stats_latency_get_bucket(count); + else + bucket = stats_latency_get_tot_bucket(count); + if (bucket[i] != 0) { + min_bucket_id = i; + break; + } + } + if (min_bucket_id != LAT_BUCKET_COUNT - 1) + break; + } + + for (uint i = LAT_BUCKET_COUNT; i > 0; i--) { + for (uint32_t count = 0; count < n_latency; ++count) { + if (state->toggle == 0) + bucket = stats_latency_get_bucket(count); + else + bucket = stats_latency_get_tot_bucket(count); + if (bucket[i - 1] != 0) { + max_bucket_id = i - 1; + break; + } + } + if (max_bucket_id) + break; + } + + if (max_bucket_id - min_bucket_id + 1 < global_nb_buckets_displayed) { + max_bucket_id = global_nb_buckets_displayed + min_bucket_id - 1; + } + + if ((global_min_bucket_id != min_bucket_id) || (global_max_bucket_id != max_bucket_id)) { + global_min_bucket_id = min_bucket_id; + global_max_bucket_id = max_bucket_id; + // Calculate how many buckets must be grouped together + if (max_bucket_id - min_bucket_id + 1 > global_nb_buckets_displayed) + group_size = ceil(1.0 * (max_bucket_id - min_bucket_id + 1) / global_nb_buckets_displayed); + else + group_size = 1; + display_latency_distr_draw_frame(state); + display_renew(); + plog_info("min_bucket_id = %d, max_bucket_id = %d\n", min_bucket_id, max_bucket_id); + } + + for (uint32_t count = 0; count < n_latency; ++count) { + if (state->toggle == 0) + bucket = stats_latency_get_bucket(count); + else + bucket = stats_latency_get_tot_bucket(count); + uint32_t i = min_bucket_id, k = 0; + uint64_t nb = 0; + while ((i < LAT_BUCKET_COUNT) && (i <= global_max_bucket_id)){ + for (uint32_t j = 0; j <= group_size; j++) + if (i + j < LAT_BUCKET_COUNT) + nb += bucket[i+j]; + display_column_print(stats_latency_distr[k++], count, "%9lu", nb); + if ((nb == 16) || (nb == 48)) + for (uint32_t j = 0; j <= group_size; j++) + plog_info("id %d: %ld\n", i+j, bucket[i+j]); + nb = 0; + i += group_size; + } + } +} + +static int display_latency_distr_get_height(void) +{ + return stats_get_n_latency(); +} + +static struct display_screen display_screen_latency_distr = { + .draw_frame = display_latency_distr_draw_frame, + .draw_stats = display_latency_distr_draw_stats, + .get_height = display_latency_distr_get_height, + .title = "latency_distr", +}; + +struct display_screen *display_latency_distr(void) +{ + return &display_screen_latency_distr; +} diff --git a/VNFs/DPPD-PROX/display_latency_distr.h b/VNFs/DPPD-PROX/display_latency_distr.h new file mode 100644 index 00000000..d22f16a4 --- /dev/null +++ b/VNFs/DPPD-PROX/display_latency_distr.h @@ -0,0 +1,23 @@ +/* +// Copyright (c) 2019 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +*/ + +#ifndef DISPLAY_LATENCY_DISTR_H +#define DISPLAY_LATENCY_DISTR_H + +struct display_screen; +struct display_screen *display_latency_distr(void); + +#endif /* DISPLAY_LATENCY_DISTR_H */ diff --git a/VNFs/DPPD-PROX/handle_lat.c b/VNFs/DPPD-PROX/handle_lat.c index a82e74ad..ef4da319 100644 --- a/VNFs/DPPD-PROX/handle_lat.c +++ b/VNFs/DPPD-PROX/handle_lat.c @@ -1,5 +1,5 @@ /* -// Copyright (c) 2010-2017 Intel Corporation +// Copyright (c) 2010-2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ #include "prox_shared.h" #include "prox_port_cfg.h" -#define DEFAULT_BUCKET_SIZE 10 +#define DEFAULT_BUCKET_SIZE 11 #define ACCURACY_BUFFER_SIZE (2 * ACCURACY_WINDOW) struct lat_info { @@ -60,7 +60,7 @@ struct delayed_latency_entry { uint32_t packet_id; uint8_t generator_id; uint64_t pkt_rx_time; - uint64_t pkt_tx_time; + uint64_t pkt_tx_time; // Time written into packets by gen. Unit is TSC >> LATENCY_ACCURACY uint64_t rx_time_err; }; @@ -128,6 +128,11 @@ static uint32_t diff_time(uint32_t rx_time, uint32_t tx_time) return rx_time - tx_time; } +uint32_t task_lat_get_latency_bucket_size(struct task_lat *task) +{ + return task->lat_test->bucket_size; +} + struct lat_test *task_lat_get_latency_meassurement(struct task_lat *task) { if (task->use_lt == task->using_lt) @@ -453,7 +458,7 @@ static void lat_test_histogram_add(struct lat_test *lat_test, uint64_t lat_tsc) uint64_t bucket_id = (lat_tsc >> lat_test->bucket_size); size_t bucket_count = sizeof(lat_test->buckets)/sizeof(lat_test->buckets[0]); - bucket_id = bucket_id < bucket_count? bucket_id : bucket_count; + bucket_id = bucket_id < bucket_count? bucket_id : (bucket_count - 1); lat_test->buckets[bucket_id]++; } @@ -776,8 +781,8 @@ static void init_task_lat(struct task_base *tbase, struct task_args *targ) task->lt[0].min_lat = -1; task->lt[1].min_lat = -1; - task->lt[0].bucket_size = targ->bucket_size - LATENCY_ACCURACY; - task->lt[1].bucket_size = targ->bucket_size - LATENCY_ACCURACY; + task->lt[0].bucket_size = targ->bucket_size; + task->lt[1].bucket_size = targ->bucket_size; if (task->unique_id_pos) { task_lat_init_eld(task, socket_id); task_lat_reset_eld(task); diff --git a/VNFs/DPPD-PROX/handle_lat.h b/VNFs/DPPD-PROX/handle_lat.h index 46f5e7d4..da164656 100644 --- a/VNFs/DPPD-PROX/handle_lat.h +++ b/VNFs/DPPD-PROX/handle_lat.h @@ -1,5 +1,5 @@ /* -// Copyright (c) 2010-2017 Intel Corporation +// Copyright (c) 2010-2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -30,6 +30,8 @@ // 8192 packets is equivalent to 550 micro-seconds at 10Gbps for 64 bytes packets #define ACCURACY_WINDOW 8192 +#define LAT_BUCKET_COUNT 128 + struct lat_test { uint64_t tot_all_pkts; uint64_t tot_pkts; @@ -44,7 +46,7 @@ struct lat_test { uint64_t tot_lat_error; unsigned __int128 var_lat_error; - uint64_t buckets[128]; + uint64_t buckets[LAT_BUCKET_COUNT]; uint64_t bucket_size; uint64_t lost_packets; }; @@ -186,6 +188,7 @@ static void lat_test_copy(struct lat_test *dst, struct lat_test *src) struct task_lat; struct lat_test *task_lat_get_latency_meassurement(struct task_lat *task); +uint32_t task_lat_get_latency_bucket_size(struct task_lat *task); void task_lat_use_other_latency_meassurement(struct task_lat *task); void task_lat_set_accuracy_limit(struct task_lat *task, uint32_t accuracy_limit_nsec); diff --git a/VNFs/DPPD-PROX/stats_latency.c b/VNFs/DPPD-PROX/stats_latency.c index 7db53f20..58bad6fa 100644 --- a/VNFs/DPPD-PROX/stats_latency.c +++ b/VNFs/DPPD-PROX/stats_latency.c @@ -1,5 +1,5 @@ /* -// Copyright (c) 2010-2017 Intel Corporation +// Copyright (c) 2010-2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ struct stats_latency_manager_entry { struct task_lat *task; + uint32_t bucket_size; uint8_t lcore_id; uint8_t task_id; struct lat_test lat_test; @@ -32,6 +33,7 @@ struct stats_latency_manager_entry { struct stats_latency_manager { uint16_t n_latency; + uint32_t bucket_size; struct stats_latency_manager_entry entries[0]; /* copy of stats when running update stats. */ }; @@ -48,6 +50,11 @@ int stats_get_n_latency(void) return slm->n_latency; } +int stats_get_latency_bucket_size(void) +{ + return slm->bucket_size; +} + uint32_t stats_latency_get_core_id(uint32_t i) { return slm->entries[i].lcore_id; @@ -63,6 +70,16 @@ struct stats_latency *stats_latency_get(uint32_t i) return &slm->entries[i].stats; } +uint64_t *stats_latency_get_bucket(uint32_t i) +{ + return slm->entries[i].lat_test.buckets; +} + +uint64_t *stats_latency_get_tot_bucket(uint32_t i) +{ + return slm->entries[i].tot_lat_test.buckets; +} + struct stats_latency *stats_latency_tot_get(uint32_t i) { return &slm->entries[i].tot; @@ -140,9 +157,14 @@ static void stats_latency_add_task(struct lcore_cfg *lconf, struct task_args *ta struct stats_latency_manager_entry *new_entry = &slm->entries[slm->n_latency]; new_entry->task = (struct task_lat *)targ->tbase; + new_entry->bucket_size = task_lat_get_latency_bucket_size(new_entry->task); new_entry->lcore_id = lconf->id; new_entry->task_id = targ->id; new_entry->tot_lat_test.min_lat = -1; + if (slm->bucket_size == 0) + slm->bucket_size = new_entry->bucket_size; + else if (slm->bucket_size != new_entry->bucket_size) + plog_err("Latency bucket size does not support different bucket sizes per task - using bucket size from first task (%d)\n", slm->bucket_size); slm->n_latency++; } diff --git a/VNFs/DPPD-PROX/stats_latency.h b/VNFs/DPPD-PROX/stats_latency.h index 83cd4a18..32f3ba34 100644 --- a/VNFs/DPPD-PROX/stats_latency.h +++ b/VNFs/DPPD-PROX/stats_latency.h @@ -1,5 +1,5 @@ /* -// Copyright (c) 2010-2017 Intel Corporation +// Copyright (c) 2010-2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -36,6 +36,8 @@ struct stats_latency { uint32_t stats_latency_get_core_id(uint32_t i); uint32_t stats_latency_get_task_id(uint32_t i); struct stats_latency *stats_latency_get(uint32_t i); +uint64_t *stats_latency_get_bucket(uint32_t i); +uint64_t *stats_latency_get_tot_bucket(uint32_t i); struct stats_latency *stats_latency_find(uint32_t lcore_id, uint32_t task_id); struct stats_latency *stats_latency_tot_get(uint32_t i); @@ -46,6 +48,7 @@ void stats_latency_update(void); void stats_latency_reset(void); int stats_get_n_latency(void); +int stats_get_latency_bucket_size(void); #ifdef LATENCY_HISTOGRAM void stats_core_lat_histogram(uint8_t lcore_id, uint8_t task_id, uint64_t **buckets); |