Commit fe1dcbc4 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] jbd: fix transaction batching

Ben points out that:

  When writing files out using O_SYNC, jbd's 1 jiffy delay results in a
  significant drop in throughput as the disk sits idle.  The patch below
  results in a 4-5x performance improvement (from 6.5MB/s to ~24-30MB/s on my
  IDE test box) when writing out files using O_SYNC.

So optimise the batching code by omitting it entirely if the process which is
doing a sync write is the same as the one which did the most recent sync
write.  If that's true, we're unlikely to get any other processes joining the
transaction.

(Has been in -mm for ages - it took me a long time to get on to performance
testing it)

Numbers, on write-cache-disabled IDE:

/usr/bin/time -p synctest -n 10 -uf -t 1 -p 1 dir-name

Unpatched:
	40 seconds
Patched:
	35 seconds
Batching disabled:
	35 seconds

This is the problematic single-process-doing-fsync case.  With multiple
fsyncing processes the numbers are AFACIT unaltered by the patch.

Aside: performance testing and instrumentation shows that the transaction
batching almost doesn't help (testing with synctest -n 1 -uf -t 100 -p 10
dir-name on non-writeback-caching IDE).  This is because by the time one
process is running a synchronous commit, a bunch of other processes already
have a transaction handle open, so they're all going to batch into the same
transaction anyway.

The batching seems to offer maybe 5-10% speedup with this workload, but I'm
pretty sure it was more important than that when it was first developed 4-odd
years ago...

Cc: "Stephen C. Tweedie" <sct@redhat.com>
Cc: Benjamin LaHaise <bcrl@kvack.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent bc5e483d
...@@ -1308,6 +1308,7 @@ int journal_stop(handle_t *handle) ...@@ -1308,6 +1308,7 @@ int journal_stop(handle_t *handle)
transaction_t *transaction = handle->h_transaction; transaction_t *transaction = handle->h_transaction;
journal_t *journal = transaction->t_journal; journal_t *journal = transaction->t_journal;
int old_handle_count, err; int old_handle_count, err;
pid_t pid;
J_ASSERT(transaction->t_updates > 0); J_ASSERT(transaction->t_updates > 0);
J_ASSERT(journal_current_handle() == handle); J_ASSERT(journal_current_handle() == handle);
...@@ -1333,8 +1334,15 @@ int journal_stop(handle_t *handle) ...@@ -1333,8 +1334,15 @@ int journal_stop(handle_t *handle)
* It doesn't cost much - we're about to run a commit and sleep * It doesn't cost much - we're about to run a commit and sleep
* on IO anyway. Speeds up many-threaded, many-dir operations * on IO anyway. Speeds up many-threaded, many-dir operations
* by 30x or more... * by 30x or more...
*
* But don't do this if this process was the most recent one to
* perform a synchronous write. We do this to detect the case where a
* single process is doing a stream of sync writes. No point in waiting
* for joiners in that case.
*/ */
if (handle->h_sync) { pid = current->pid;
if (handle->h_sync && journal->j_last_sync_writer != pid) {
journal->j_last_sync_writer = pid;
do { do {
old_handle_count = transaction->t_handle_count; old_handle_count = transaction->t_handle_count;
schedule_timeout_uninterruptible(1); schedule_timeout_uninterruptible(1);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#define jfs_debug jbd_debug #define jfs_debug jbd_debug
#else #else
#include <linux/types.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/journal-head.h> #include <linux/journal-head.h>
#include <linux/stddef.h> #include <linux/stddef.h>
...@@ -618,6 +619,7 @@ struct transaction_s ...@@ -618,6 +619,7 @@ struct transaction_s
* @j_wbuf: array of buffer_heads for journal_commit_transaction * @j_wbuf: array of buffer_heads for journal_commit_transaction
* @j_wbufsize: maximum number of buffer_heads allowed in j_wbuf, the * @j_wbufsize: maximum number of buffer_heads allowed in j_wbuf, the
* number that will fit in j_blocksize * number that will fit in j_blocksize
* @j_last_sync_writer: most recent pid which did a synchronous write
* @j_private: An opaque pointer to fs-private information. * @j_private: An opaque pointer to fs-private information.
*/ */
...@@ -807,6 +809,8 @@ struct journal_s ...@@ -807,6 +809,8 @@ struct journal_s
struct buffer_head **j_wbuf; struct buffer_head **j_wbuf;
int j_wbufsize; int j_wbufsize;
pid_t j_last_sync_writer;
/* /*
* An opaque pointer to fs-private information. ext3 puts its * An opaque pointer to fs-private information. ext3 puts its
* superblock pointer here * superblock pointer here
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment