Commit 67c9684f authored by Liu Bo's avatar Liu Bo Committed by Chris Mason
Browse files

Btrfs: improve multi-thread buffer read

While testing with my buffer read fio jobs[1], I find that btrfs does not
perform well enough.

Here is a scenario in fio jobs:

We have 4 threads, "t1 t2 t3 t4", starting to buffer read a same file,
and all of them will race on add_to_page_cache_lru(), and if one thread
successfully puts its page into the page cache, it takes the responsibility
to read the page's data.

And what's more, reading a page needs a period of time to finish, in which
other threads can slide in and process rest pages:

     t1          t2          t3          t4
   add Page1
   read Page1  add Page2
     |         read Page2  add Page3
     |            |        read Page3  add Page4
     |            |           |        read Page4
     v            v           v           v
    bio          bio         bio         bio

Now we have four bios, each of which holds only one page since we need to
maintain consecutive pages in bio.  Thus, we can end up with far more bios
than we need.

Here we're going to
a) delay the real read-page section and
b) try to put more pages into page cache.

With that said, we can make each bio hold more pages and reduce the number
of bios we need.

Here is some numbers taken from fio results:
         w/o patch                 w patch
       -------------  --------  ---------------
READ:    745MB/s        +25%       934MB/s


Signed-off-by: default avatarLiu Bo <>
Signed-off-by: default avatarJosef Bacik <>
parent df57dbe6
...@@ -3566,19 +3566,38 @@ int extent_readpages(struct extent_io_tree *tree, ...@@ -3566,19 +3566,38 @@ int extent_readpages(struct extent_io_tree *tree,
struct bio *bio = NULL; struct bio *bio = NULL;
unsigned page_idx; unsigned page_idx;
unsigned long bio_flags = 0; unsigned long bio_flags = 0;
struct page *pagepool[16];
struct page *page;
int i = 0;
int nr = 0;
for (page_idx = 0; page_idx < nr_pages; page_idx++) { for (page_idx = 0; page_idx < nr_pages; page_idx++) {
struct page *page = list_entry(pages->prev, struct page, lru); page = list_entry(pages->prev, struct page, lru);
prefetchw(&page->flags); prefetchw(&page->flags);
list_del(&page->lru); list_del(&page->lru);
if (!add_to_page_cache_lru(page, mapping, if (add_to_page_cache_lru(page, mapping,
page->index, GFP_NOFS)) { page->index, GFP_NOFS)) {
__extent_read_full_page(tree, page, get_extent, page_cache_release(page);
pagepool[nr++] = page;
if (nr < ARRAY_SIZE(pagepool))
for (i = 0; i < nr; i++) {
__extent_read_full_page(tree, pagepool[i], get_extent,
&bio, 0, &bio_flags); &bio, 0, &bio_flags);
} }
page_cache_release(page); nr = 0;
} }
for (i = 0; i < nr; i++) {
__extent_read_full_page(tree, pagepool[i], get_extent,
&bio, 0, &bio_flags);
BUG_ON(!list_empty(pages)); BUG_ON(!list_empty(pages));
if (bio) if (bio)
return submit_one_bio(READ, bio, 0, bio_flags); return submit_one_bio(READ, bio, 0, bio_flags);
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