mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Nest: Add tests and benchmark for FIB
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.
This commit is contained in:
parent
d44409f0c0
commit
6c2979f4d0
@ -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);
|
||||
|
@ -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)
|
||||
|
246
nest/rt-fib_test.c
Normal file
246
nest/rt-fib_test.c
Normal file
@ -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();
|
||||
}
|
@ -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, ...);
|
||||
|
||||
|
132
test/bt-utils.c
132
test/bt-utils.c
@ -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;
|
||||
}
|
||||
|
@ -26,6 +26,12 @@
|
||||
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user