Add Latency Distribution display 67/69467/3
authorXavier Simonart <xavier.simonart@intel.com>
Sun, 29 Dec 2019 17:41:50 +0000 (18:41 +0100)
committerXavier Simonart <xavier.simonart@intel.com>
Wed, 29 Jan 2020 11:12:27 +0000 (12:12 +0100)
- 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>
VNFs/DPPD-PROX/Makefile
VNFs/DPPD-PROX/cmd_parser.c
VNFs/DPPD-PROX/display.c
VNFs/DPPD-PROX/display.h
VNFs/DPPD-PROX/display_latency_distr.c [new file with mode: 0644]
VNFs/DPPD-PROX/display_latency_distr.h [new file with mode: 0644]
VNFs/DPPD-PROX/handle_lat.c
VNFs/DPPD-PROX/handle_lat.h
VNFs/DPPD-PROX/stats_latency.c
VNFs/DPPD-PROX/stats_latency.h

index bc11f5d..f8bde42 100644 (file)
@@ -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
index a8fe3a0..3e71c56 100644 (file)
@@ -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
 }
 
index d7421e8..e1b8d8d 100644 (file)
@@ -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();
index 4b51754..4c9f9ba 100644 (file)
@@ -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 (file)
index 0000000..9808a70
--- /dev/null
@@ -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 (file)
index 0000000..d22f16a
--- /dev/null
@@ -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 */
index a82e74a..ef4da31 100644 (file)
@@ -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);
index 46f5e7d..da16465 100644 (file)
@@ -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);
 
index 7db53f2..58bad6f 100644 (file)
@@ -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++;
 }
 
index 83cd4a1..32f3ba3 100644 (file)
@@ -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);