]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Nest: Add tests and benchmark for FIB
authorOndrej Zajicek <santiago@crfreenet.org>
Tue, 16 May 2023 11:25:48 +0000 (13:25 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Tue, 16 May 2023 11:25:48 +0000 (13:25 +0200)
Basic fib_get() / fib_find() test for random prefixes, FIB_WALK() test,
and benchmark for fib_find(). Also generalize and reuse some code from
trie tests.

filter/trie_test.c
nest/Makefile
nest/rt-fib_test.c [new file with mode: 0644]
test/birdtest.h
test/bt-utils.c
test/bt-utils.h

index dc791280d08871145524c4ef7a3543963f04802b..5724e49fa17c642c90c323be5a39297409b37fa3 100644 (file)
@@ -28,12 +28,6 @@ struct f_prefix_node {
   struct f_prefix prefix;
 };
 
-static u32
-xrandom(u32 max)
-{
-  return (bt_random() % max);
-}
-
 static inline uint
 get_exp_random(void)
 {
@@ -95,27 +89,10 @@ is_prefix_included(list *prefixes, const net_addr *needle)
   return 0; /* FAIL */
 }
 
-static void
-get_random_net(net_addr *net, int v6)
-{
-  if (!v6)
-  {
-    uint pxlen = xrandom(24)+8;
-    ip4_addr ip4 = ip4_from_u32((u32) bt_random());
-    net_fill_ip4(net, ip4_and(ip4, ip4_mkmask(pxlen)), pxlen);
-  }
-  else
-  {
-    uint pxlen = xrandom(120)+8;
-    ip6_addr ip6 = ip6_build(bt_random(), bt_random(), bt_random(), bt_random());
-    net_fill_ip6(net, ip6_and(ip6, ip6_mkmask(pxlen)), pxlen);
-  }
-}
-
 static void
 get_random_prefix(struct f_prefix *px, int v6, int tight)
 {
-  get_random_net(&px->net, v6);
+  bt_random_net(&px->net, !v6 ? NET_IP4 : NET_IP6);
 
   if (tight)
   {
@@ -379,7 +356,7 @@ select_random_prefix_subset(list *src[], net_addr dst[], int sn, int dn)
     struct f_prefix_node *px;
     WALK_LIST(px, *src[i])
     {
-      if (xrandom(rnd) != 0)
+      if (bt_random_n(rnd) != 0)
        continue;
 
       net_copy(&dst[n], &px->prefix.net);
@@ -395,7 +372,7 @@ done:
   /* Shuffle networks */
   for (int i = 0; i < dn; i++)
   {
-    int j = xrandom(dn);
+    int j = bt_random_n(dn);
 
     if (i == j)
       continue;
@@ -444,7 +421,7 @@ t_match_random_net(void)
     for (int i = 0; i < PREFIX_TESTS_NUM; i++)
     {
       net_addr net;
-      get_random_net(&net, v6);
+      bt_random_net(&net, !v6 ? NET_IP4 : NET_IP6);
       test_match_net(prefixes, trie, &net);
     }
 
@@ -828,7 +805,7 @@ t_trie_walk_to_root(void)
     for (i = 0; i < (PREFIX_TESTS_NUM / 10); i++)
     {
       net_addr from;
-      get_random_net(&from, v6);
+      bt_random_net(&from, !v6 ? NET_IP4 : NET_IP6);
 
       net_addr found[129];
       int found_num = find_covering_nets(pxset, num, &from, found);
index 163a1199462e6772aeec5c52f1abd7bd11317134..5a244c7569e33e4be1b3ec903894f9b9405b46b9 100644 (file)
@@ -9,6 +9,6 @@ $(o)proto-build.c: Makefile $(lastword $(MAKEFILE_LIST)) $(objdir)/.dir-stamp
 
 prepare: $(o)proto-build.c
 
-tests_src := a-set_test.c a-path_test.c
+tests_src := a-set_test.c a-path_test.c rt-fib_test.c
 tests_targets := $(tests_targets) $(tests-target-files)
 tests_objs := $(tests_objs) $(src-o-files)
diff --git a/nest/rt-fib_test.c b/nest/rt-fib_test.c
new file mode 100644 (file)
index 0000000..2dd7ce8
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *     BIRD -- Forwarding Information Base -- Tests
+ *
+ *     (c) 2023 CZ.NIC z.s.p.o.
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "test/birdtest.h"
+#include "test/bt-utils.h"
+
+#include "nest/route.h"
+
+
+#define TESTS_NUM              10
+#define PREFIXES_NUM           400000
+#define PREFIX_TESTS_NUM       200000
+#define PREFIX_BENCH_MAX       1000000
+#define PREFIX_BENCH_NUM       10000000
+
+struct test_node
+{
+  int pos;
+  struct fib_node n;
+};
+
+static inline int net_match(struct test_node *tn, net_addr *query, net_addr *data)
+{ return (tn->pos < PREFIXES_NUM) && net_equal(query, &data[tn->pos]); }
+
+static int
+t_match_random_net(void)
+{
+  bt_bird_init();
+  bt_config_parse(BT_CONFIG_SIMPLE);
+
+  for (int round = 0; round < TESTS_NUM; round++)
+  {
+    int type = !(round & 1) ? NET_IP4 : NET_IP6;
+
+    pool *p = rp_new(&root_pool, "FIB pool");
+    net_addr *nets = bt_random_nets(type, PREFIXES_NUM);
+
+    /* Make FIB structure */
+    struct fib f;
+    fib_init(&f, &root_pool, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 4, NULL);
+
+    for (int i = 0; i < PREFIXES_NUM; i++)
+    {
+      struct test_node *tn = fib_get(&f, &nets[i]);
+      bt_assert(!tn->pos || net_match(tn, &nets[i], nets));
+      tn->pos = i;
+    }
+
+    /* Test (mostly) negative matches */
+    for (int i = 0; i < PREFIX_TESTS_NUM; i++)
+    {
+      net_addr net;
+      bt_random_net(&net, type);
+
+      struct test_node *tn = fib_find(&f, &net);
+      bt_assert(!tn || net_match(tn, &net, nets));
+    }
+
+    /* Test positive matches */
+    for (int i = 0; i < PREFIX_TESTS_NUM; i++)
+    {
+      int j = bt_random_n(PREFIXES_NUM);
+
+      struct test_node *tn = fib_find(&f, &nets[j]);
+      bt_assert(tn && net_match(tn, &nets[j], nets));
+    }
+
+    rfree(p);
+    tmp_flush();
+  }
+
+  bt_bird_cleanup();
+  return 1;
+}
+
+static int
+t_fib_walk(void)
+{
+  bt_bird_init();
+  bt_config_parse(BT_CONFIG_SIMPLE);
+
+  for (int round = 0; round < TESTS_NUM; round++)
+  {
+    int type = !(round & 1) ? NET_IP4 : NET_IP6;
+
+    pool *p = rp_new(&root_pool, "FIB pool");
+    net_addr *nets = bt_random_nets(type, PREFIXES_NUM);
+    byte *marks = tmp_allocz(PREFIXES_NUM);
+
+    /* Make FIB structure */
+    struct fib f;
+    fib_init(&f, p, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 4, NULL);
+
+    for (int i = 1; i < PREFIXES_NUM; i++)
+    {
+      struct test_node *tn = fib_get(&f, &nets[i]);
+      bt_assert(!tn->pos || net_match(tn, &nets[i], nets));
+      if (tn->pos)
+      {
+       /* Mark dupicate nets */
+       bt_assert(!marks[tn->pos]);
+       marks[tn->pos] = 1;
+      }
+      tn->pos = i;
+    }
+
+    /* Walk FIB and mark nets */
+    FIB_WALK(&f, struct test_node, tn)
+    {
+      bt_assert(!marks[tn->pos]);
+      marks[tn->pos] = 1;
+    }
+    FIB_WALK_END;
+
+    /* Check in all nets are marked */
+    for (int i = 1; i < PREFIXES_NUM; i++)
+      bt_assert(marks[i]);
+
+    rfree(p);
+    tmp_flush();
+  }
+
+  bt_bird_cleanup();
+  return 1;
+}
+
+static int
+benchmark_fib_dataset(const char *filename, int type)
+{
+  net_addr *nets, *test_r, *test_s;
+  uint n = PREFIX_BENCH_MAX;
+  int tn = PREFIX_BENCH_NUM;
+  int match;
+
+  bt_reset_suite_case_timer();
+  bt_log_suite_case_result(1, "Reading %s", filename, n);
+  nets = bt_read_net_file(filename, type, &n);
+  bt_log_suite_case_result(1, "Read net data, %u nets", n);
+  bt_reset_suite_case_timer();
+
+  pool *p = rp_new(&root_pool, "FIB pool");
+
+  /* Make FIB structure */
+  struct fib f;
+  fib_init(&f, p, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 0, NULL);
+
+  for (int i = 0; i < (int) n; i++)
+  {
+    struct test_node *tn = fib_get(&f, &nets[i]);
+    tn->pos = i;
+  }
+
+  bt_log_suite_case_result(1, "Fill FIB structure, %u nets, order %u", n, f.hash_order);
+  bt_reset_suite_case_timer();
+
+  /* Compute FIB size */
+  size_t fib_size = rmemsize(p).effective * 1000 / (1024*1024);
+  bt_log_suite_case_result(1, "FIB size: %u.%03u MB", (uint) (fib_size / 1000), (uint) (fib_size % 1000));
+
+  /* Compute FIB histogram */
+  uint hist[16] = {};
+  uint sum = 0;
+  for (uint i = 0; i < f.hash_size; i++)
+  {
+    int len = 0;
+    for (struct fib_node *fn = f.hash_table[i]; fn; fn = fn->next)
+      len++;
+
+    sum += len;
+    len = MIN(len, 15);
+    hist[len]++;
+  }
+  bt_log_suite_case_result(1, "FIB histogram:");
+  for (uint i = 0; i < 16; i++)
+    if (hist[i])
+      bt_log_suite_case_result(1, "%02u: %8u", i, hist[i]);
+
+  uint avg = (sum * 1000) / (f.hash_size - hist[0]);
+  bt_log_suite_case_result(1, "FIB chain length: %u.%03u", (uint) (avg / 1000), (uint) (avg % 1000));
+  bt_reset_suite_case_timer();
+
+  /* Make test data */
+  test_r = bt_random_nets(type, tn);
+  test_s = bt_random_net_subset(nets, n, tn);
+
+  bt_log_suite_case_result(1, "Make test data, 2x %u nets", tn);
+  bt_reset_suite_case_timer();
+
+  /* Test (mostly negative) random matches */
+  match = 0;
+  for (int i = 0; i < tn; i++)
+    if (fib_find(&f, &test_r[i]))
+      match++;
+
+  bt_log_suite_case_result(1, "Random match, %d / %d matches", match, tn);
+  bt_reset_suite_case_timer();
+
+  /* Test (positive) subset matches */
+  match = 0;
+  for (int i = 0; i < tn; i++)
+    if (fib_find(&f, &test_s[i]))
+      match++;
+
+  bt_log_suite_case_result(1, "Subset match, %d / %d matches", match, tn);
+  bt_log_suite_case_result(1, "");
+  bt_reset_suite_case_timer();
+
+  rfree(p);
+  tmp_flush();
+  return 1;
+}
+
+static int UNUSED
+t_bench_fib_datasets(void)
+{
+  bt_bird_init();
+  bt_config_parse(BT_CONFIG_SIMPLE);
+
+  /* Specific datasets, not included */
+  benchmark_fib_dataset("fib-data-bgp-v4-1",  NET_IP4);
+  benchmark_fib_dataset("fib-data-bgp-v4-10", NET_IP4);
+  benchmark_fib_dataset("fib-data-bgp-v6-1",  NET_IP6);
+  benchmark_fib_dataset("fib-data-bgp-v6-10", NET_IP6);
+
+  bt_bird_cleanup();
+
+  return 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+  bt_init(argc, argv);
+
+  bt_test_suite(t_match_random_net, "Testing random prefix matching");
+  bt_test_suite(t_fib_walk, "Testing FIB_WALK() on random FIB");
+
+  // bt_test_suite(t_bench_fib_datasets, "Benchmark FIB from datasets by random subset of nets");
+
+  return bt_exit_value();
+}
index cfeebb98700373ffb47901eb13d5ce75056b6767..540092d6436f532253b277d10df7892295b84241 100644 (file)
@@ -37,6 +37,10 @@ int bt_test_suite_base(int (*test_fn)(const void *), const char *test_id, const
 static inline u64 bt_random(void)
 { return ((u64) random() & 0xffffffff) | ((u64) random() << 32); }
 
+static inline u32 bt_random_n(u32 max)
+{ return random() % max; }
+
+
 void bt_log_suite_result(int result, const char *fmt, ...);
 void bt_log_suite_case_result(int result, const char *fmt, ...);
 
index 8496e185ac54a96e8adf9ef5c699df2b864e208e..fb42cd35c4083de69c7bdafd5c43ecdc05fcc4e1 100644 (file)
@@ -219,3 +219,135 @@ bt_bytes_to_hex(char *buf, const byte *in_data, size_t size)
     sprintf(buf + i*2, "%02x", in_data[i]);
 }
 
+void
+bt_random_net(net_addr *net, int type)
+{
+  ip4_addr ip4;
+  ip6_addr ip6;
+  uint pxlen;
+
+  switch (type)
+  {
+  case NET_IP4:
+    pxlen = bt_random_n(24)+8;
+    ip4 = ip4_from_u32((u32) bt_random());
+    net_fill_ip4(net, ip4_and(ip4, ip4_mkmask(pxlen)), pxlen);
+    break;
+
+  case NET_IP6:
+    pxlen = bt_random_n(120)+8;
+    ip6 = ip6_build(bt_random(), bt_random(), bt_random(), bt_random());
+    net_fill_ip6(net, ip6_and(ip6, ip6_mkmask(pxlen)), pxlen);
+    break;
+
+  default:
+    die("Net type %d not implemented", type);
+  }
+}
+
+net_addr *
+bt_random_nets(int type, uint n)
+{
+  net_addr *nets = tmp_alloc(n * sizeof(net_addr));
+
+  for (uint i = 0; i < n; i++)
+    bt_random_net(&nets[i], type);
+
+  return nets;
+}
+
+net_addr *
+bt_random_net_subset(net_addr *src, uint sn, uint dn)
+{
+  net_addr *nets = tmp_alloc(dn * sizeof(net_addr));
+
+  for (uint i = 0; i < dn; i++)
+    net_copy(&nets[i], &src[bt_random_n(sn)]);
+
+  return nets;
+}
+
+void
+bt_read_net(const char *str, net_addr *net, int type)
+{
+  ip4_addr ip4;
+  ip6_addr ip6;
+  uint pxlen;
+  char addr[64];
+
+  switch (type)
+  {
+  case NET_IP4:
+    if (sscanf(str, "%[0-9.]/%u", addr, &pxlen) != 2)
+      goto err;
+
+    if (!ip4_pton(addr, &ip4))
+      goto err;
+
+    if (!net_validate_px4(ip4, pxlen))
+      goto err;
+
+    net_fill_ip4(net, ip4, pxlen);
+    break;
+
+  case NET_IP6:
+    if (sscanf(str, "%[0-9a-fA-F:.]/%u", addr, &pxlen) != 2)
+      goto err;
+
+    if (!ip6_pton(addr, &ip6))
+      goto err;
+
+    if (!net_validate_px6(ip6, pxlen))
+      goto err;
+
+    net_fill_ip6(net, ip6, pxlen);
+    break;
+
+  default:
+    die("Net type %d not implemented", type);
+  }
+  return;
+
+err:
+  bt_abort_msg("Invalid network '%s'", str);
+}
+
+net_addr *
+bt_read_nets(FILE *f, int type, uint *n)
+{
+  char str[80];
+
+  net_addr *nets = tmp_alloc(*n * sizeof(net_addr));
+  uint i = 0;
+
+  errno = 0;
+  while (fgets(str, sizeof(str), f))
+  {
+    if (str[0] == '\n')
+      break;
+
+    if (i >= *n)
+      bt_abort_msg("Too many networks");
+
+    bt_read_net(str, &nets[i], type);
+    bt_debug("ADD %s\n", str);
+    i++;
+  }
+  bt_syscall(errno, "fgets()");
+
+  bt_debug("DONE reading %u nets\n", i);
+
+  *n = i;
+  return nets;
+}
+
+net_addr *
+bt_read_net_file(const char *filename, int type, uint *n)
+{
+  FILE *f = fopen(filename, "r");
+  bt_syscall(!f, "fopen(%s)", filename);
+  net_addr *nets = bt_read_nets(f, type, n);
+  fclose(f);
+
+  return nets;
+}
index 13d267cc1c1710a38184934fc1b257269db935a8..d29a0b7c8d2015087a640ccbb124f62516723092 100644 (file)
 
 uint bt_naive_pow(uint base, uint power);
 void bt_bytes_to_hex(char *buf, const byte *in_data, size_t size);
+void bt_random_net(net_addr *net, int type);
+net_addr *bt_random_nets(int type, uint n);
+net_addr *bt_random_net_subset(net_addr *src, uint sn, uint dn);
+void bt_read_net(const char *str, net_addr *net, int type);
+net_addr *bt_read_nets(FILE *f, int type, uint *n);
+net_addr *bt_read_net_file(const char *filename, int type, uint *n);
 
 void bt_bird_init(void);
 void bt_bird_cleanup(void);