Hi devs,
this is part of the delta_files() speedup patch series:
http://svn.haxx.se/dev/archive-2010-03/0604.shtml
Under Windows, a significant part of the overall runtime
is spent in writing data to the (already buffered) APR
file implementation. It seems that the mutex serializing
the buffer access in apr_file_write is expensive.
Also, >50% of all write requests are 2 bytes or smaller
(i.e. line endings and empty lines). For them, the deep
call hierarchy constitutes a large overhead on register-
lacking x86.
This patch eliminates far over 90% of all write requests
bringing the portion of time spent in _svn_io_file_write_full
down from about 7 to below 3 percent on 32 bit Windows.
Performance gain is ~1% under Linux but due to the
larger async I/O and mutex overhead it is about 4%
under Windows:
s~$ time ~/1.7-928181/svn export --ignore-externals -q $REPO/trunk /dev/shm/t
real 0m3.727s
user 0m3.189s
sys 0m0.542s
~$ time ~/1.7-patched/svn export --ignore-externals -q $REPO/trunk /dev/shm/t
real 0m3.697s
user 0m3.148s
sys 0m0.558s
-- Stefan^2.
[[[
Buffer small write requests to APR file streams. For details see
http:// ...
* subversion/libsvn_subr/stream.c
(baton_apr): add write buffer members
(flush_buffer_apr): new function
(read_handler_apr): auto-allocate write buffer and re-direct
small writes there.
(read_handler_apr, close_handler_apr, mark_handler_apr,
seek_handler_apr): flush write buffer before calling i/o functions.
patch by stefanfuhrmann < at > alice-dsl.de
]]]
Index: subversion/libsvn_subr/stream.c
===================================================================
--- subversion/libsvn_subr/stream.c (revision 928181)
+++ subversion/libsvn_subr/stream.c (working copy)
@@ -667,15 +667,45 @@
* When either of these is negative, no range has been specified. */
apr_off_t start;
apr_off_t end;
+
+ /* Collect short write requests here. You must clear this
+ * buffer before any other APR i/o call for this file.
+ * Initially NULL, will be auto-allocated just before use. */
+ char *write_buffer;
+ apr_size_t buffered;
};
+/* Ensure that no data is left in the write buffer so that
+ the file contents is actually up-to-date and following
+ APR i/o calls will return the expected data.
+ */
+static svn_error_t *
+flush_buffer_apr(struct baton_apr *btn)
+{
+ if (btn->buffered > 0)
+ {
+ apr_size_t written = 0;
+ SVN_ERR(svn_io_file_write_full(btn->file,
+ btn->write_buffer,
+ btn->buffered,
+ &written,
+ btn->pool));
+ btn->buffered -= written;
+ }
+ if (btn->buffered > 0)
+ return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
+
+ return SVN_NO_ERROR;
+}
+
static svn_error_t *
read_handler_apr(void *baton, char *buffer, apr_size_t *len)
{
struct baton_apr *btn = baton;
svn_error_t *err;
+ SVN_ERR(flush_buffer_apr(btn));
err = svn_io_file_read_full(btn->file, buffer, *len, len, btn->pool);
if (err && APR_STATUS_IS_EOF(err->apr_err))
{
@@ -686,19 +716,62 @@
return err;
}
+/* typical code lines should get into the buffer */
+#define BUFFER_THRESHOLD (128)
+#define BUFFER_SIZE (8*BUFFER_THRESHOLD)
+
static svn_error_t *
write_handler_apr(void *baton, const char *data, apr_size_t *len)
{
struct baton_apr *btn = baton;
+ apr_size_t temp = *len;
+ /* better safe than sorry ... */
+ if (temp == 0)
+ return SVN_NO_ERROR;
+
+ /* Buffer short requests here as the underlying APR implementation
+ * needs to employ mutexes etc. When processing source files,
+ * virtually all write requests get buffered here. */
+ if (temp < BUFFER_THRESHOLD)
+ {
+ if (btn->write_buffer == NULL)
+ btn->write_buffer = apr_palloc(btn->pool,BUFFER_SIZE);
+
+ if (btn->buffered + temp > BUFFER_SIZE)
+ SVN_ERR(flush_buffer_apr(btn));
+
+ /* In some cases, line endings account for 50+% of all
+ write requests. Make them as cheap as possible. */
+ if (temp > 2)
+ memcpy(btn->write_buffer + btn->buffered, data, temp);
+ else
+ {
+ char* dest = btn->write_buffer + btn->buffered;
+ dest[0] = data[0];
+
+ if (temp == 2)
+ dest[1] = data[1];
+ }
+
+ btn->buffered += temp;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(flush_buffer_apr(btn));
return svn_io_file_write_full(btn->file, data, *len, len, btn->pool);
}
+/* pre-processor cleanup - not strictly necessary */
+#undef BUFFER_THRESHOLD
+#undef BUFFER_SIZE
+
static svn_error_t *
close_handler_apr(void *baton)
{
struct baton_apr *btn = baton;
+ SVN_ERR(flush_buffer_apr(btn));
return svn_io_file_close(btn->file, btn->pool);
}
@@ -721,6 +794,7 @@
struct baton_apr *btn = baton;
(*mark) = apr_palloc(pool, sizeof(**mark));
(*mark)->m.apr = 0;
+ SVN_ERR(flush_buffer_apr(btn));
SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &(*mark)->m.apr, btn->pool));
return SVN_NO_ERROR;
}
@@ -729,6 +803,7 @@
seek_handler_apr(void *baton, svn_stream_mark_t *mark)
{
struct baton_apr *btn = baton;
+ SVN_ERR(flush_buffer_apr(btn));
SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &mark->m.apr, btn->pool));
return SVN_NO_ERROR;
}
@@ -804,6 +879,8 @@
baton->pool = pool;
baton->start = -1;
baton->end = -1;
+ baton->write_buffer = NULL;
+ baton->buffered = 0;
stream = svn_stream_create(baton, pool);
svn_stream_set_read(stream, read_handler_apr);
svn_stream_set_write(stream, write_handler_apr);