apr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From i...@apache.org
Subject svn commit: r1806308 - in /apr/apr/trunk: file_io/win32/readwrite.c test/testfile.c
Date Sat, 26 Aug 2017 15:20:03 GMT
Author: ivan
Date: Sat Aug 26 15:20:02 2017
New Revision: 1806308

URL: http://svn.apache.org/viewvc?rev=1806308&view=rev
Log:
Win32: Improve apr_file_write() performance on buffered files by reducing
the amount of WriteFile() calls for large writes.

Previously, writing has been implemented with a loop that keeps copying the
data to the internal 4KB buffer and writing this buffer to disk by calling
WriteFile(4096).  This patch reduces the amount of syscalls for large writes
by performing them with a single syscall, if possible.  If the buffer is
not empty at the moment when the large write occurs, it is first filled
up to its 4KB capacity, flushed, and the remaining part of the data is
written with a single syscall.

* file_io/win32/readwrite.c
  (write_buffered): Within the write loop, check if we have a situation
   with an empty buffer and a large chunk pending to be written.  In this
   case, bypass the buffering and write the remaining chunk with a single
   syscall.  Return an appropriate number of written bytes to satisfy
   the apr_file_write() function contract.
  (apr_file_write): Adjust call to write_buffered().

* test/testfile.c
  (test_large_write_buffered,
   test_two_large_writes_buffered,
   test_small_and_large_writes_buffered,
   test_write_buffered_spanning_over_bufsize): New tests.
  (testfile): Run the new tests.

Patch by: Evgeny Kotkov <evgeny.kotkov {at} visualsvn.com>

Modified:
    apr/apr/trunk/file_io/win32/readwrite.c
    apr/apr/trunk/test/testfile.c

Modified: apr/apr/trunk/file_io/win32/readwrite.c
URL: http://svn.apache.org/viewvc/apr/apr/trunk/file_io/win32/readwrite.c?rev=1806308&r1=1806307&r2=1806308&view=diff
==============================================================================
--- apr/apr/trunk/file_io/win32/readwrite.c (original)
+++ apr/apr/trunk/file_io/win32/readwrite.c Sat Aug 26 15:20:02 2017
@@ -290,9 +290,8 @@ static apr_status_t write_helper(HANDLE
 }
 
 static apr_status_t write_buffered(apr_file_t *thefile, const char *buf,
-                                   apr_size_t len)
+                                   apr_size_t len, apr_size_t *pwritten)
 {
-    apr_size_t blocksize;
     apr_status_t rv;
 
     if (thefile->direction == 0) {
@@ -306,20 +305,41 @@ static apr_status_t write_buffered(apr_f
         thefile->direction = 1;
     }
 
-    rv = 0;
-    while (rv == 0 && len > 0) {
-        if (thefile->bufpos == thefile->bufsize)   /* write buffer is full */
-            rv = apr_file_flush(thefile);
+    *pwritten = 0;
 
-        blocksize = len > thefile->bufsize - thefile->bufpos ?
-                    thefile->bufsize - thefile->bufpos : len;
-        memcpy(thefile->buffer + thefile->bufpos, buf, blocksize);
-        thefile->bufpos += blocksize;
-        buf += blocksize;
-        len -= blocksize;
+    while (len > 0) {
+        if (thefile->bufpos == thefile->bufsize) { /* write buffer is full */
+            rv = apr_file_flush(thefile);
+            if (rv) {
+                return rv;
+            }
+        }
+        /* If our buffer is empty, and we cannot fit the remaining chunk
+         * into it, write the chunk with a single syscall and return.
+         */
+        if (thefile->bufpos == 0 && len > thefile->bufsize) {
+            apr_size_t written;
+
+            rv = write_helper(thefile->filehand, buf, len, &written);
+            thefile->filePtr += written;
+            *pwritten += written;
+            return rv;
+        }
+        else {
+            apr_size_t blocksize = len;
+
+            if (blocksize > thefile->bufsize - thefile->bufpos) {
+                blocksize = thefile->bufsize - thefile->bufpos;
+            }
+            memcpy(thefile->buffer + thefile->bufpos, buf, blocksize);
+            thefile->bufpos += blocksize;
+            buf += blocksize;
+            len -= blocksize;
+            *pwritten += blocksize;
+        }
     }
 
-    return rv;
+    return APR_SUCCESS;
 }
 
 APR_DECLARE(apr_status_t) apr_file_write(apr_file_t *thefile, const void *buf, apr_size_t
*nbytes)

Modified: apr/apr/trunk/test/testfile.c
URL: http://svn.apache.org/viewvc/apr/apr/trunk/test/testfile.c?rev=1806308&r1=1806307&r2=1806308&view=diff
==============================================================================
--- apr/apr/trunk/test/testfile.c (original)
+++ apr/apr/trunk/test/testfile.c Sat Aug 26 15:20:02 2017
@@ -1446,6 +1446,188 @@ static void test_append(abts_case *tc, v
     apr_file_close(f2);
 }
 
+static void test_large_write_buffered(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *f;
+    const char *fname = "data/testlarge_write_buffered.dat";
+    apr_size_t len;
+    apr_size_t bytes_written;
+    apr_size_t bytes_read;
+    char *buf;
+    char *buf2;
+
+    apr_file_remove(fname, p);
+
+    rv = apr_file_open(&f, fname,
+                       APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_BUFFERED,
+                       APR_FPROT_OS_DEFAULT, p);
+    APR_ASSERT_SUCCESS(tc, "open test file for writing", rv);
+
+    /* Test a single large write. */
+    len = 80000;
+    buf = apr_palloc(p, len);
+    memset(buf, 'a', len);
+    rv = apr_file_write_full(f, buf, len, &bytes_written);
+    APR_ASSERT_SUCCESS(tc, "write to file", rv);
+    ABTS_INT_EQUAL(tc, (int)len, (int)bytes_written);
+    apr_file_close(f);
+
+    rv = apr_file_open(&f, fname, APR_FOPEN_READ,
+                       APR_FPROT_OS_DEFAULT, p);
+    APR_ASSERT_SUCCESS(tc, "open test file for reading", rv);
+
+    buf2 = apr_palloc(p, len + 1);
+    rv = apr_file_read_full(f, buf2, len + 1, &bytes_read);
+    ABTS_INT_EQUAL(tc, APR_EOF, rv);
+    ABTS_INT_EQUAL(tc, (int)len, (int)bytes_read);
+    ABTS_TRUE(tc, memcmp(buf, buf2, len) == 0);
+    apr_file_close(f);
+
+    apr_file_remove(fname, p);
+}
+
+static void test_two_large_writes_buffered(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *f;
+    const char *fname = "data/testtwo_large_writes_buffered.dat";
+    apr_size_t len;
+    apr_size_t bytes_written;
+    apr_size_t bytes_read;
+    char *buf;
+    char *buf2;
+
+    apr_file_remove(fname, p);
+
+    rv = apr_file_open(&f, fname,
+                       APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_BUFFERED,
+                       APR_FPROT_OS_DEFAULT, p);
+    APR_ASSERT_SUCCESS(tc, "open test file for writing", rv);
+
+    /* Test two consecutive large writes. */
+    len = 80000;
+    buf = apr_palloc(p, len);
+    memset(buf, 'a', len);
+
+    rv = apr_file_write_full(f, buf, len / 2, &bytes_written);
+    APR_ASSERT_SUCCESS(tc, "write to file", rv);
+    ABTS_INT_EQUAL(tc, (int)(len / 2), (int)bytes_written);
+
+    rv = apr_file_write_full(f, buf, len / 2, &bytes_written);
+    APR_ASSERT_SUCCESS(tc, "write to file", rv);
+    ABTS_INT_EQUAL(tc, (int)(len / 2), (int)bytes_written);
+
+    apr_file_close(f);
+
+    rv = apr_file_open(&f, fname, APR_FOPEN_READ,
+                       APR_FPROT_OS_DEFAULT, p);
+    APR_ASSERT_SUCCESS(tc, "open test file for reading", rv);
+
+    buf2 = apr_palloc(p, len + 1);
+    rv = apr_file_read_full(f, buf2, len + 1, &bytes_read);
+    ABTS_INT_EQUAL(tc, APR_EOF, rv);
+    ABTS_INT_EQUAL(tc, (int) len, (int)bytes_read);
+    ABTS_TRUE(tc, memcmp(buf, buf2, len) == 0);
+    apr_file_close(f);
+
+    apr_file_remove(fname, p);
+}
+
+static void test_small_and_large_writes_buffered(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *f;
+    const char *fname = "data/testtwo_large_writes_buffered.dat";
+    apr_size_t len;
+    apr_size_t bytes_written;
+    apr_size_t bytes_read;
+    char *buf;
+    char *buf2;
+
+    apr_file_remove(fname, p);
+
+    rv = apr_file_open(&f, fname,
+                       APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_BUFFERED,
+                       APR_FPROT_OS_DEFAULT, p);
+    APR_ASSERT_SUCCESS(tc, "open test file for writing", rv);
+
+    /* Test small write followed by a large write. */
+    len = 80000;
+    buf = apr_palloc(p, len);
+    memset(buf, 'a', len);
+
+    rv = apr_file_write_full(f, buf, 5, &bytes_written);
+    APR_ASSERT_SUCCESS(tc, "write to file", rv);
+    ABTS_INT_EQUAL(tc, 5, (int)bytes_written);
+
+    rv = apr_file_write_full(f, buf, len - 5, &bytes_written);
+    APR_ASSERT_SUCCESS(tc, "write to file", rv);
+    ABTS_INT_EQUAL(tc, (int)(len - 5), (int)bytes_written);
+
+    apr_file_close(f);
+
+    rv = apr_file_open(&f, fname, APR_FOPEN_READ,
+                       APR_FPROT_OS_DEFAULT, p);
+    APR_ASSERT_SUCCESS(tc, "open test file for reading", rv);
+
+    buf2 = apr_palloc(p, len + 1);
+    rv = apr_file_read_full(f, buf2, len + 1, &bytes_read);
+    ABTS_INT_EQUAL(tc, APR_EOF, rv);
+    ABTS_INT_EQUAL(tc, (int) len, (int)bytes_read);
+    ABTS_TRUE(tc, memcmp(buf, buf2, len) == 0);
+    apr_file_close(f);
+
+    apr_file_remove(fname, p);
+}
+
+static void test_write_buffered_spanning_over_bufsize(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *f;
+    const char *fname = "data/testwrite_buffered_spanning_over_bufsize.dat";
+    apr_size_t len;
+    apr_size_t bytes_written;
+    apr_size_t bytes_read;
+    char *buf;
+    char *buf2;
+
+    apr_file_remove(fname, p);
+
+    rv = apr_file_open(&f, fname,
+                       APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_BUFFERED,
+                       APR_FPROT_OS_DEFAULT, p);
+    APR_ASSERT_SUCCESS(tc, "open test file for writing", rv);
+
+    /* Test three writes than span over the default buffer size. */
+    len = APR_BUFFERSIZE + 1;
+    buf = apr_palloc(p, len);
+    memset(buf, 'a', len);
+
+    rv = apr_file_write_full(f, buf, APR_BUFFERSIZE - 1, &bytes_written);
+    APR_ASSERT_SUCCESS(tc, "write to file", rv);
+    ABTS_INT_EQUAL(tc, APR_BUFFERSIZE - 1, (int)bytes_written);
+
+    rv = apr_file_write_full(f, buf, 2, &bytes_written);
+    APR_ASSERT_SUCCESS(tc, "write to file", rv);
+    ABTS_INT_EQUAL(tc, 2, (int)bytes_written);
+
+    apr_file_close(f);
+
+    rv = apr_file_open(&f, fname, APR_FOPEN_READ,
+                       APR_FPROT_OS_DEFAULT, p);
+    APR_ASSERT_SUCCESS(tc, "open test file for reading", rv);
+
+    buf2 = apr_palloc(p, len + 1);
+    rv = apr_file_read_full(f, buf2, len + 1, &bytes_read);
+    ABTS_INT_EQUAL(tc, APR_EOF, rv);
+    ABTS_INT_EQUAL(tc, (int)len, (int)bytes_read);
+    ABTS_TRUE(tc, memcmp(buf, buf2, len) == 0);
+    apr_file_close(f);
+
+    apr_file_remove(fname, p);
+}
+
 abts_suite *testfile(abts_suite *suite)
 {
     suite = ADD_SUITE(suite)
@@ -1495,6 +1677,10 @@ abts_suite *testfile(abts_suite *suite)
     abts_run_test(suite, test_buffer_set_get, NULL);
     abts_run_test(suite, test_xthread, NULL);
     abts_run_test(suite, test_append, NULL);
+    abts_run_test(suite, test_large_write_buffered, NULL);
+    abts_run_test(suite, test_two_large_writes_buffered, NULL);
+    abts_run_test(suite, test_small_and_large_writes_buffered, NULL);
+    abts_run_test(suite, test_write_buffered_spanning_over_bufsize, NULL);
 
     return suite;
 }



Mime
View raw message