[ovs-dev] [PATCH] ovn-controller: optimize lex_token memory usage

Huang Lei 148012453 at qq.com
Fri Mar 25 00:55:07 UTC 2016


From: Huang Lei <lhuang8 at ebay.com>

During our scalability test '2k HVs + 20k lports' we found that lexer is a
major user of heap memory:
-   5.22%  ovn-controller  libjemalloc.so.1    [.] free
   - free
      + 27.46% lexer_get
      + 18.00% ofctrl_put
      ...
-   1.85%  ovn-controller  libjemalloc.so.1    [.] malloc
   - malloc
   - xmalloc
      - 55.03% xmemdup0
         - 90.58% lex_parse_id.isra.0
            - lexer_get
      ...

So lex_token is modified to usage a 'buffer' defined in it for tokens smaller
than 128 bytes, and for tokens bigger than 128 bytes it turn to use heap
memory. This change makes our test case run at least 10% faster.

Signed-off-by: Huang Lei <lhuang8 at ebay.com>
---
 ovn/lib/lex.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 ovn/lib/lex.h | 11 +++++++++
 2 files changed, 84 insertions(+), 4 deletions(-)

diff --git a/ovn/lib/lex.c b/ovn/lib/lex.c
index 9cad5c7..6dba223 100644
--- a/ovn/lib/lex.c
+++ b/ovn/lib/lex.c
@@ -50,13 +50,20 @@ lex_token_init(struct lex_token *token)
 {
     token->type = LEX_T_END;
     token->s = NULL;
+    token->source = LEX_TOKEN_FIXED;
 }

 /* Frees memory owned by 'token'. */
 void
 lex_token_destroy(struct lex_token *token)
 {
-    free(token->s);
+    if (token->source == LEX_TOKEN_MALLOC) {
+        ovs_assert(token->s != NULL);
+        free(token->s);
+        token->source = LEX_TOKEN_FIXED;
+    }
+
+    token->s = NULL;
 }

 /* Exchanges 'a' and 'b'. */
@@ -66,6 +73,66 @@ lex_token_swap(struct lex_token *a, struct lex_token *b)
     struct lex_token tmp = *a;
     *a = *b;
     *b = tmp;
+
+    if (a->source == LEX_TOKEN_FIXED && a->s != NULL) {
+        a->s = a->buffer;
+    }
+
+    if (b->source == LEX_TOKEN_FIXED && b->s != NULL) {
+        b->s = b->buffer;
+    }
+}
+
+/* the string 's' may not terminate by '\0' at 'length'. */
+void
+lex_token_strcpy(struct lex_token *token, const char *s, size_t length)
+{
+    if (token->source == LEX_TOKEN_FIXED) {
+        if (length + 1 <= sizeof(token->buffer)) {
+            memcpy(token->buffer, s, length);
+            token->buffer[length] = '\0';
+            token->s = token->buffer;
+            return;
+        }
+
+        token->source = LEX_TOKEN_MALLOC;
+        token->s = xmemdup0(s, length);
+    } else {
+        token->s = xrealloc(token->s, length + 1);
+        memcpy(token->s, s, length);
+        token->s[length] = '\0';
+    }
+}
+
+void
+lex_token_strset(struct lex_token *token, char *s)
+{
+    token->source = LEX_TOKEN_MALLOC;
+    token->s = s;
+}
+
+void
+lex_token_vsprintf(struct lex_token *token, const char *format, va_list args)
+{
+    va_list args2;
+    size_t length;
+
+    va_copy(args2, args);
+    length = vsnprintf(NULL, 0, format, args);
+
+    if (token->source == LEX_TOKEN_FIXED) {
+        if (length + 1 <= sizeof(token->buffer)) {
+            token->s = token->buffer;
+        } else {
+            token->source = LEX_TOKEN_MALLOC;
+            token->s = xmalloc(length + 1);
+        }
+    } else {
+        token->s = xrealloc(token->s, length + 1);
+    }
+
+    vsnprintf(token->s, length + 1, format, args2);
+    va_end(args2);
 }
 
 /* lex_token_format(). */
@@ -261,7 +328,7 @@ lex_error(struct lex_token *token, const char *message, ...)

     va_list args;
     va_start(args, message);
-    token->s = xvasprintf(message, args);
+    lex_token_vsprintf(token, message, args);
     va_end(args);
 }

@@ -428,6 +495,7 @@ static const char *
 lex_parse_string(const char *p, struct lex_token *token)
 {
     const char *start = ++p;
+    char * s = NULL;
     for (;;) {
         switch (*p) {
         case '\0':
@@ -435,8 +503,9 @@ lex_parse_string(const char *p, struct lex_token *token)
             return p;

         case '"':
-            token->type = (json_string_unescape(start, p - start, &token->s)
+            token->type = (json_string_unescape(start, p - start, &s)
                            ? LEX_T_STRING : LEX_T_ERROR);
+            lex_token_strset(token, s);
             return p + 1;

         case '\\':
@@ -476,7 +545,7 @@ lex_parse_id(const char *p, struct lex_token *token)
     } while (lex_is_idn(*p));

     token->type = LEX_T_ID;
-    token->s = xmemdup0(start, p - start);
+    lex_token_strcpy(token, start, p - start);
     return p;
 }

diff --git a/ovn/lib/lex.h b/ovn/lib/lex.h
index 3c2bb6a..ae3a4ad 100644
--- a/ovn/lib/lex.h
+++ b/ovn/lib/lex.h
@@ -77,6 +77,11 @@ enum lex_format {
 };
 const char *lex_format_to_string(enum lex_format);

+enum lex_token_source {
+    LEX_TOKEN_FIXED, /* use the buffer defined in lex_token */
+    LEX_TOKEN_MALLOC, /* expand into head */
+};
+
 /* A token.
  *
  * 's' is owned by the token. */
@@ -86,11 +91,17 @@ struct lex_token {
     enum lex_format format;     /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */
     union mf_subvalue value;    /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */
     union mf_subvalue mask;     /* LEX_T_MASKED_INTEGER only. */
+    enum lex_token_source source;
+    char buffer[128];           /* pointed by' s' while buffer_source is
+                                 * LEX_TOKEN_FIXED */
 };

 void lex_token_init(struct lex_token *);
 void lex_token_destroy(struct lex_token *);
 void lex_token_swap(struct lex_token *, struct lex_token *);
+void lex_token_strcpy(struct lex_token *, const char *s, size_t length);
+void lex_token_strset(struct lex_token *, char *s);
+void lex_token_vsprintf(struct lex_token *, const char *format, va_list args);

 void lex_token_format(const struct lex_token *, struct ds *);
 const char *lex_token_parse(struct lex_token *, const char *input,
--
1.9.1






More information about the dev mailing list