Author: rhuijben
Date: Wed May 30 16:52:36 2012
New Revision: 1344347
URL: http://svn.apache.org/viewvc?rev=1344347&view=rev
Log:
Remove about 15% of the cpu overhead of svnserve and httpd while running the
test suite by introducing a very simple parser buffer in the config file parser
code.
The nice clean stream translation code had an 7 times overhead on this very
performance critical code path, while clients of svnserve and httpd were
waiting. (Before this patch the this code path used 22% of the cpu time,
now just 3%)
* subversion/libsvn_subr/config_file.c
(parse_context_t): Add buffer and position variables. Use EOF as no ungetc
var.
(parser_getc): Skip '\r' characters as we only need the '\n' character for our
config files. Use EOF as no ungetc var.
(parse_option,
parse_section_name): Rename pool argument to scratch_pool.
(svn_config__parse_file): Create scratch pool and store the file and parse
ctx in it. Update initialization and destroy scratch_pool after successfull
parsing.
Modified:
subversion/trunk/subversion/libsvn_subr/config_file.c
Modified: subversion/trunk/subversion/libsvn_subr/config_file.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/config_file.c?rev=1344347&r1=1344346&r2=1344347&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/config_file.c (original)
+++ subversion/trunk/subversion/libsvn_subr/config_file.c Wed May 30 16:52:36
2012
@@ -60,15 +60,19 @@ typedef struct parse_context_t
/* The current line in the file */
int line;
- /* Cached ungotten character - streams don't support ungetc()
- [emulate it] */
+ /* Emulate an ungetc */
int ungotten_char;
- svn_boolean_t have_ungotten_char;
- /* Temporary strings, allocated from the temp pool */
+ /* Temporary strings */
svn_stringbuf_t *section;
svn_stringbuf_t *option;
svn_stringbuf_t *value;
+
+ /* Parser buffer for getc() to avoid call overhead into several libraries
+ for every character */
+ char parser_buffer[SVN_STREAM_CHUNK_SIZE]; /* Larger than most config files
*/
+ size_t buffer_pos; /* Current position within parser_buffer */
+ size_t buffer_size; /* parser_buffer contains this many bytes */
} parse_context_t;
@@ -82,23 +86,36 @@ typedef struct parse_context_t
static APR_INLINE svn_error_t *
parser_getc(parse_context_t *ctx, int *c)
{
- if (ctx->have_ungotten_char)
- {
- *c = ctx->ungotten_char;
- ctx->have_ungotten_char = FALSE;
- }
- else
+ do
{
- char char_buf;
- apr_size_t readlen = 1;
+ if (ctx->ungotten_char != EOF)
+ {
+ *c = ctx->ungotten_char;
+ ctx->ungotten_char = EOF;
+ }
+ else if (ctx->buffer_pos < ctx->buffer_size)
+ {
+ *c = ctx->parser_buffer[ctx->buffer_pos];
+ ctx->buffer_pos++;
+ }
+ else
+ {
+ ctx->buffer_pos = 0;
+ ctx->buffer_size = sizeof(ctx->parser_buffer);
- SVN_ERR(svn_stream_read(ctx->stream, &char_buf, &readlen));
+ SVN_ERR(svn_stream_read(ctx->stream, ctx->parser_buffer,
+ &(ctx->buffer_size)));
- if (readlen == 1)
- *c = char_buf;
- else
- *c = EOF;
+ if (ctx->buffer_pos < ctx->buffer_size)
+ {
+ *c = ctx->parser_buffer[ctx->buffer_pos];
+ ctx->buffer_pos++;
+ }
+ else
+ *c = EOF;
+ }
}
+ while (*c == '\r');
return SVN_NO_ERROR;
}
@@ -111,7 +128,6 @@ static APR_INLINE svn_error_t *
parser_ungetc(parse_context_t *ctx, int c)
{
ctx->ungotten_char = c;
- ctx->have_ungotten_char = TRUE;
return SVN_NO_ERROR;
}
@@ -241,7 +257,7 @@ parse_value(int *pch, parse_context_t *c
/* Parse a single option */
static svn_error_t *
-parse_option(int *pch, parse_context_t *ctx, apr_pool_t *pool)
+parse_option(int *pch, parse_context_t *ctx, apr_pool_t *scratch_pool)
{
svn_error_t *err = SVN_NO_ERROR;
int ch;
@@ -260,7 +276,7 @@ parse_option(int *pch, parse_context_t *
ch = EOF;
err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
"%s:%d: Option must end with ':' or '='",
- svn_dirent_local_style(ctx->file, pool),
+ svn_dirent_local_style(ctx->file, scratch_pool),
ctx->line);
}
else
@@ -284,7 +300,8 @@ parse_option(int *pch, parse_context_t *
* starts a section name.
*/
static svn_error_t *
-parse_section_name(int *pch, parse_context_t *ctx, apr_pool_t *pool)
+parse_section_name(int *pch, parse_context_t *ctx,
+ apr_pool_t *scratch_pool)
{
svn_error_t *err = SVN_NO_ERROR;
int ch;
@@ -303,7 +320,7 @@ parse_section_name(int *pch, parse_conte
ch = EOF;
err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
"%s:%d: Section header must end with ']'",
- svn_dirent_local_style(ctx->file, pool),
+ svn_dirent_local_style(ctx->file, scratch_pool),
ctx->line);
}
else
@@ -363,91 +380,101 @@ svn_config__sys_config_path(const char *
svn_error_t *
svn_config__parse_file(svn_config_t *cfg, const char *file,
- svn_boolean_t must_exist, apr_pool_t *pool)
+ svn_boolean_t must_exist, apr_pool_t *result_pool)
{
svn_error_t *err = SVN_NO_ERROR;
- parse_context_t ctx;
+ parse_context_t *ctx;
int ch, count;
svn_stream_t *stream;
+ apr_pool_t *scratch_pool = svn_pool_create(result_pool);
- err = svn_stream_open_readonly(&stream, file, pool, pool);
+ err = svn_stream_open_readonly(&stream, file, scratch_pool, scratch_pool);
if (! must_exist && err && APR_STATUS_IS_ENOENT(err->apr_err))
{
svn_error_clear(err);
+ svn_pool_destroy(scratch_pool);
return SVN_NO_ERROR;
}
else
SVN_ERR(err);
- ctx.cfg = cfg;
- ctx.file = file;
- ctx.stream = svn_subst_stream_translated(stream, "\n", TRUE, NULL, FALSE,
- pool);
- ctx.line = 1;
- ctx.have_ungotten_char = FALSE;
- ctx.section = svn_stringbuf_create_empty(pool);
- ctx.option = svn_stringbuf_create_empty(pool);
- ctx.value = svn_stringbuf_create_empty(pool);
+ ctx = apr_palloc(scratch_pool, sizeof(*ctx));
+
+ ctx->cfg = cfg;
+ ctx->file = file;
+ ctx->stream = stream;
+ ctx->line = 1;
+ ctx->ungotten_char = EOF;
+ ctx->section = svn_stringbuf_create_empty(scratch_pool);
+ ctx->option = svn_stringbuf_create_empty(scratch_pool);
+ ctx->value = svn_stringbuf_create_empty(scratch_pool);
+ ctx->buffer_pos = 0;
+ ctx->buffer_size = 0;
do
{
- SVN_ERR(skip_whitespace(&ctx, &ch, &count));
+ SVN_ERR(skip_whitespace(ctx, &ch, &count));
switch (ch)
{
case '[': /* Start of section header */
if (count == 0)
- SVN_ERR(parse_section_name(&ch, &ctx, pool));
+ SVN_ERR(parse_section_name(&ch, ctx, scratch_pool));
else
return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
"%s:%d: Section header"
" must start in the first column",
- svn_dirent_local_style(file, pool),
- ctx.line);
+ svn_dirent_local_style(file,
+ scratch_pool),
+ ctx->line);
break;
case '#': /* Comment */
if (count == 0)
{
- SVN_ERR(skip_to_eoln(&ctx, &ch));
- ++ctx.line;
+ SVN_ERR(skip_to_eoln(ctx, &ch));
+ ++(ctx->line);
}
else
return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
"%s:%d: Comment"
" must start in the first column",
- svn_dirent_local_style(file, pool),
- ctx.line);
+ svn_dirent_local_style(file,
+ scratch_pool),
+ ctx->line);
break;
case '\n': /* Empty line */
- ++ctx.line;
+ ++(ctx->line);
break;
case EOF: /* End of file or read error */
break;
default:
- if (svn_stringbuf_isempty(ctx.section))
+ if (svn_stringbuf_isempty(ctx->section))
return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
"%s:%d: Section header expected",
- svn_dirent_local_style(file, pool),
- ctx.line);
+ svn_dirent_local_style(file,
+ scratch_pool),
+ ctx->line);
else if (count != 0)
return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
"%s:%d: Option expected",
- svn_dirent_local_style(file, pool),
- ctx.line);
+ svn_dirent_local_style(file,
+ scratch_pool),
+ ctx->line);
else
- SVN_ERR(parse_option(&ch, &ctx, pool));
+ SVN_ERR(parse_option(&ch, ctx, scratch_pool));
break;
}
}
while (ch != EOF);
/* Close the streams (and other cleanup): */
- return svn_stream_close(ctx.stream);
+ svn_pool_destroy(scratch_pool);
+ return SVN_NO_ERROR;
}