Commit dae0ea9b authored by Jan Maria Matejka's avatar Jan Maria Matejka
Browse files

Trie optimizer

This patch optimizes prefix sets like
  [ 10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24 ]
into
  [ 10.0.0.0/22{24,24} ]

This should improve config loading speed with large config files.

Works only in config-check mode; the appropriate flag is -O.
Beware. The output is written directly on stdout.

This patch is not intended to be ever merged with mainline in this form.
Instead, we should fix bad lexer performance and also merge the
merge-able prefixes on-the-fly during trie creation. Ultimately,
we should have better possibility to feed database-like structures into
BIRD instead of config file.
parent abec2008
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -727,7 +727,13 @@ constant:
 | fprefix_s {NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->a1.p = val; *val = $1; }
 | RTRID  { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_QUAD;  $$->a2.i = $1; }
 | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(FI_CONSTANT); $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
 | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET;  $$->a2.p = $2; }
 | '[' fprefix_set ']' {
      $$ = f_new_inst(FI_CONSTANT);
      $$->aux = T_PREFIX_SET;
      $$->a2.p = $2;
      if (trie_optimize_flag)
	trie_optimize($$);
    }
 | ENUM	  { $$ = f_new_inst(FI_CONSTANT); $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
 ;

+2 −0
Original line number Diff line number Diff line
@@ -298,6 +298,8 @@ struct f_trie
  struct f_trie_node root[0];		/* Root trie node follows */
};

void trie_optimize(struct f_inst *);

#define NEW_F_VAL struct f_val * val; val = cfg_alloc(sizeof(struct f_val));

#define FF_FORCE_TMPATTR 1		/* Force all attributes to be temporary */
+79 −0
Original line number Diff line number Diff line
@@ -70,10 +70,14 @@
 */

#include "nest/bird.h"
#include "lib/resource.h"
#include "lib/string.h"
#include "conf/conf.h"
#include "filter/filter.h"

#include <stdio.h>
#include <stdlib.h>

/**
 * f_new_trie - allocates and returns a new empty trie
 * @lp: linear pool to allocate items from
@@ -181,6 +185,10 @@ trie_add_prefix(struct f_trie *t, ip_addr px, int plen, int l, int h)
	  return n;
	}

      /* All additional prefixes are already covered by this node. */
      if (ipa_equal(ipa_and(n->accept, amask), amask))
	return n;

      /* Update accept mask part M2 and go deeper */
      n->accept = ipa_or(n->accept, ipa_and(amask, n->mask));

@@ -258,6 +266,77 @@ trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2)
  return trie_node_same(t1->c[0], t2->c[0]) && trie_node_same(t1->c[1], t2->c[1]);
}

static int
trie_node_optimize(struct f_trie_node *t)
{
  int ret = 0;
  if (t->c[0]) ret |= trie_node_optimize(t->c[0]);
  if (t->c[1]) ret |= trie_node_optimize(t->c[1]);

  if ((!t->c[0]) || (!t->c[1])) return ret;

  if (t->c[0]->plen != t->plen + 1) return ret;
  if (t->c[1]->plen != t->plen + 1) return ret;

  ip_addr cmask = ipa_and(t->c[0]->accept, t->c[1]->accept);
  if (ipa_zero(cmask)) return ret;

  ip_addr lmask = ipa_xor(t->c[0]->accept, cmask);
  ip_addr rmask = ipa_xor(t->c[1]->accept, cmask);

  if (!ipa_zero(lmask) && !ipa_zero(rmask))
    return ret;

  t->c[0]->accept = lmask;
  t->c[1]->accept = rmask;

  t->accept = ipa_or(t->accept, cmask);
  return 1;
}

static int
trie_node_count(struct f_trie_node *t)
{
  int ret = 0;
  if (t->c[0]) ret += trie_node_count(t->c[0]);
  if (t->c[1]) ret += trie_node_count(t->c[1]);

  for (
      ip_addr amask = t->accept;
      ipa_nonzero(amask);
      ret++,
      amask = ipa_xor(amask, ipa_bitrange(amask, NULL, NULL))
      );

  return ret;
}

void
trie_optimize(struct f_inst *what)
{
  struct f_trie *t = what->a2.p;
  if (!t || !t->root)
    return;

  if (!trie_node_optimize(t->root))
    return;

  int size = trie_node_count(t->root) * (STD_ADDRESS_P_LENGTH + 11);
  char *buf = xmalloc(size);
  buffer b = {
    .start = buf,
    .pos = buf,
    .end = buf + size
  };

  printf("Prefix set in file %s at line %d: ", ifs->file_name, ifs->lino);

  trie_format(t, &b);
  buffer_puts(&b, "\n");
  fputs(b.start, stdout);
  xfree(buf);
}

/**
 * trie_same
 * @t1: first trie to be compared
+7 −1
Original line number Diff line number Diff line
@@ -619,12 +619,13 @@ signal_init(void)
 *	Parsing of command-line arguments
 */

static char *opt_list = "c:dD:ps:P:u:g:flRh";
static char *opt_list = "c:dD:pOs:P:u:g:flRh";
static int parse_and_exit;
char *bird_name;
static char *use_user;
static char *use_group;
static int run_in_foreground = 0;
int trie_optimize_flag = 0;

static void
display_usage(void)
@@ -649,6 +650,7 @@ display_help(void)
    "  -h, --help           Display this information\n"
    "  -l                   Look for a configuration file and a communication socket\n"
    "                       file in the current working directory\n"
    "  -O		    Optimize prefix sets; implies -p\n"
    "  -p                   Test configuration file and exit without start\n"
    "  -P <pid-file>        Create a PID file with given filename\n"
    "  -R                   Apply graceful restart recovery after start\n"
@@ -758,6 +760,10 @@ parse_args(int argc, char **argv)
      case 'p':
	parse_and_exit = 1;
	break;
      case 'O':
	parse_and_exit = 1;
	trie_optimize_flag = 1;
	break;
      case 's':
	path_control_socket = optarg;
	socket_changed = 1;
+1 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ struct birdsock;
/* main.c */

extern char *bird_name;
extern int trie_optimize_flag;
void async_config(void);
void async_dump(void);
void async_shutdown(void);