From: Huang Lei <[email protected]> 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. Tested with 'ovn -- lexer' case. Signed-off-by: Huang Lei <[email protected]> --- ovn/lib/lex.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- ovn/lib/lex.h | 15 +++++++++--- 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/ovn/lib/lex.c b/ovn/lib/lex.c index 9cad5c7..f18fe3d 100644 --- a/ovn/lib/lex.c +++ b/ovn/lib/lex.c @@ -56,7 +56,11 @@ lex_token_init(struct lex_token *token) void lex_token_destroy(struct lex_token *token) { - free(token->s); + if (token->s && token->s != token->buffer) { + free(token->s); + } + + token->s = NULL; } /* Exchanges 'a' and 'b'. */ @@ -66,6 +70,70 @@ lex_token_swap(struct lex_token *a, struct lex_token *b) struct lex_token tmp = *a; *a = *b; *b = tmp; + + /* before swap, if 's' was pointed to 'buffer', its value shall be + * changed to point to the 'buffer' with the copied value. + */ + if (a->s == b->buffer) { + a->s = a->buffer; + } + + if (b->s == a->buffer) { + 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->s == NULL || token->s == token->buffer) { + if (length + 1 <= sizeof(token->buffer)) { + memcpy(token->buffer, s, length); + token->buffer[length] = '\0'; + token->s = token->buffer; + return; + } + + 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) +{ + if (token->s && token->s != token->buffer) { + free(token->s); + } + + 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->s == NULL || token->s == token->buffer) { + if (length + 1 <= sizeof(token->buffer)) { + token->s = token->buffer; + } else { + 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 +329,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 +496,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 +504,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 +546,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..4f78c09 100644 --- a/ovn/lib/lex.h +++ b/ovn/lib/lex.h @@ -79,18 +79,27 @@ const char *lex_format_to_string(enum lex_format); /* A token. * - * 's' is owned by the token. */ + * 's' may point to 'buffer'; otherwise, it points to malloc()ed memory owned + * by the token. */ struct lex_token { enum lex_type type; /* One of LEX_*. */ char *s; /* LEX_T_ID, LEX_T_STRING, LEX_T_ERROR only. */ 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. */ + union { + struct { + union mf_subvalue value; /* LEX_T_INTEGER, LEX_T_MASKED_INTEGER only. */ + union mf_subvalue mask; /* LEX_T_MASKED_INTEGER only. */ + }; + char buffer[256]; /* Buffer for LEX_T_ID/LEX_T_STRING. */ + }; }; 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 _______________________________________________ dev mailing list [email protected] http://openvswitch.org/mailman/listinfo/dev
