[or-cvs] r11738: Fix disgusting O(n^2) behavior in router_parse_list_from_str (in tor/trunk: . src/or)

nickm at seul.org nickm at seul.org
Tue Oct 2 01:22:42 UTC 2007


Author: nickm
Date: 2007-10-01 21:22:42 -0400 (Mon, 01 Oct 2007)
New Revision: 11738

Modified:
   tor/trunk/
   tor/trunk/ChangeLog
   tor/trunk/src/or/routerparse.c
Log:
 r15436 at catbus:  nickm | 2007-10-01 21:17:27 -0400
 Fix disgusting O(n^2) behavior in router_parse_list_from_string.  Noticed by Li-Hui Zhou; found with oprofile.



Property changes on: tor/trunk
___________________________________________________________________
 svk:merge ticket from /tor/trunk [r15436] on 8246c3cf-6607-4228-993b-4d95d33730f1

Modified: tor/trunk/ChangeLog
===================================================================
--- tor/trunk/ChangeLog	2007-10-01 22:24:01 UTC (rev 11737)
+++ tor/trunk/ChangeLog	2007-10-02 01:22:42 UTC (rev 11738)
@@ -12,6 +12,12 @@
     - Use annotations to record the source for each descriptor.
     - Use annotations to record the purpose of each descriptor.
 
+  o Major bugfixes (performance):
+    - Fix really bad O(n^2) performance when parsing a long list of routers:
+      Instead of searching the entire list for an "extra-info " string which
+      usually wasn't there, once for every routerinfo we read, just scan
+      lines forward until we find one we like.  Bugfix on 0.2.0.1.
+
   o Minor bugfixes (controller):
     - When sending a status event to the controller telling it that an
       OR address is readable, set the port correctly.  (Previously we

Modified: tor/trunk/src/or/routerparse.c
===================================================================
--- tor/trunk/src/or/routerparse.c	2007-10-01 22:24:01 UTC (rev 11737)
+++ tor/trunk/src/or/routerparse.c	2007-10-02 01:22:42 UTC (rev 11738)
@@ -855,6 +855,40 @@
   return 0;
 }
 
+/** DOCDOC */
+static int
+find_start_of_next_router_or_extrainfo(const char **s_ptr,
+                                       const char *eos,
+                                       int *is_extrainfo_out)
+{
+  const char *annotations = NULL;
+  const char *s = *s_ptr;
+
+  s = eat_whitespace_eos(s, eos);
+
+  while (s < eos-32) {  /* 32 gives enough room for a the first keyword. */
+    /* We're at the start of a line. */
+    tor_assert(*s != '\n');
+
+    if (*s == '@' && !annotations) {
+      annotations = s;
+    } else if (*s == 'r' && !strcmpstart(s, "router ")) {
+      *s_ptr = annotations ? annotations : s;
+      *is_extrainfo_out = 0;
+      return 0;
+    } else if (*s == 'e' && !strcmpstart(s, "extra-info ")) {
+      *s_ptr = annotations ? annotations : s;
+      *is_extrainfo_out = 1;
+      return 0;
+    }
+
+    if (!(s = memchr(s+1, '\n', eos-(s+1))))
+      break;
+    s = eat_whitespace_eos(s, eos);
+  }
+  return -1;
+}
+
 /** Given a string *<b>s</b> containing a concatenated sequence of router
  * descriptors (or extra-info documents if <b>is_extrainfo</b> is set), parses
  * them and stores the result in <b>dest</b>.  All routers are marked running
@@ -893,40 +927,9 @@
   tor_assert(eos >= *s);
 
   while (1) {
-    *s = eat_whitespace_eos(*s, eos);
-    if ((eos - *s) < 32) /* make sure it's long enough. */
+    if (find_start_of_next_router_or_extrainfo(s, eos, &have_extrainfo) < 0)
       break;
 
-    /* Don't start parsing the rest of *s unless it contains a router or
-     * extra-info. */
-    if (strcmpstart(*s, "extra-info ")==0) {
-      have_extrainfo = 1;
-    } else if (strcmpstart(*s, "router ")==0) {
-      have_extrainfo = 0;
-    } else {
-      /* skip junk. */
-      const char *annotation = NULL, *ei, *ri;
-      if (**s == '@') {
-        annotation = *s;
-      } else {
-        if ((annotation = tor_memstr(*s, eos-*s, "\n@")))
-          ++annotation;
-      }
-
-      ei = tor_memstr(*s, eos-*s, "\nextra-info ");
-      ri = tor_memstr(*s, eos-*s, "\nrouter ");
-      if (ri && (!ei || ri < ei)) {
-        have_extrainfo = 0;
-        *s = ri + 1;
-      } else if (ei) {
-        have_extrainfo = 1;
-        *s = ei + 1;
-      } else {
-        break;
-      }
-      if (annotation && annotation < *s)
-        *s = annotation;
-    }
     end = tor_memstr(*s, eos-*s, "\nrouter-signature");
     if (end)
       end = tor_memstr(end, eos-end, "\n-----END SIGNATURE-----\n");



More information about the tor-commits mailing list