Optimise readng large number --server options at start up.

When re-reading upstream servers from /etc/resolv.conf or other
sources that can change dnsmasq tries to avoid memory fragmentation by
re-using existing records that are being re-read unchanged. This
involves seaching all the server records for each new one installed.
During startup this search is pointless, and can cause long start
times with thousands of --server options because the work needed is
O(n^2). Handle this case more intelligently.  Thanks to Ye Zhou for
spotting the problem and an initial patch.
This commit is contained in:
Simon Kelley
2022-11-21 16:40:53 +00:00
parent d6d7527c95
commit 881eaa4dbc
3 changed files with 49 additions and 21 deletions

View File

@@ -49,6 +49,16 @@ version 2.88
in the domain it attests to be returned as unvalidated, and not in the domain it attests to be returned as unvalidated, and not
as a validation error. as a validation error.
Optimise reading large numbers of --server options. When re-reading
upstream servers from /etc/resolv.conf or other sources that
can change dnsmasq tries to avoid memory fragmentation by re-using
existing records that are being re-read unchanged. This involves
seaching all the server records for each new one installed.
During startup this search is pointless, and can cause long
start times with thousands of --server options because the work
needed is O(n^2). Handle this case more intelligently.
Thanks to Ye Zhou for spotting the problem and an initial patch.
version 2.87 version 2.87
Allow arbitrary prefix lengths in --rev-server and Allow arbitrary prefix lengths in --rev-server and

View File

@@ -546,11 +546,23 @@ static int order_qsort(const void *a, const void *b)
return rc; return rc;
} }
/* When loading large numbers of server=.... lines during startup,
there's no possibility that there will be server records that can be reused, but
searching a long list for each server added grows as O(n^2) and slows things down.
This flag is set only if is known there may be free server records that can be reused.
There's a call to mark_servers(0) in read_opts() to reset the flag before
main config read. */
static int maybe_free_servers = 0;
/* Must be called before add_update_server() to set daemon->servers_tail */ /* Must be called before add_update_server() to set daemon->servers_tail */
void mark_servers(int flag) void mark_servers(int flag)
{ {
struct server *serv, **up; struct server *serv, **up;
maybe_free_servers = !!flag;
daemon->servers_tail = NULL; daemon->servers_tail = NULL;
/* mark everything with argument flag */ /* mark everything with argument flag */
@@ -668,6 +680,9 @@ int add_update_server(int flags,
be at the end. */ be at the end. */
struct server **up, *tmp; struct server **up, *tmp;
serv = NULL;
if (maybe_free_servers)
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{ {
tmp = serv->next; tmp = serv->next;

View File

@@ -5754,6 +5754,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
add_txt("servers.bind", NULL, TXT_STAT_SERVERS); add_txt("servers.bind", NULL, TXT_STAT_SERVERS);
#endif #endif
/* See comment above make_servers(). Optimises server-read code. */
mark_servers(0);
while (1) while (1)
{ {
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG