diff options
author | Eric Biggers <ebiggers@google.com> | 2017-07-10 18:47:29 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-07-10 19:32:30 -0400 |
commit | 241f01fbeda2521f802eeef4de0261387e6e9c1d (patch) | |
tree | 1ea0ec512f4f55fe1ade4c030d6b255f85c0e41a /fs/buffer.c | |
parent | 3457f4147675108aa83f9f33c136f06bb9f8518f (diff) |
fs/buffer.c: make bh_lru_install() more efficient
To install a buffer_head into the cpu's LRU queue, bh_lru_install()
would construct a new copy of the queue and then memcpy it over the real
queue. But it's easily possible to do the update in-place, which is
faster and simpler. Some work can also be skipped if the buffer_head
was already in the queue.
As a microbenchmark I timed how long it takes to run sb_getblk()
10,000,000 times alternating between BH_LRU_SIZE + 1 blocks.
Effectively, this benchmarks looking up buffer_heads that are in the
page cache but not in the LRU:
Before this patch: 1.758s
After this patch: 1.653s
This patch also removes about 350 bytes of compiled code (on x86_64),
partly due to removal of the memcpy() which was being inlined+unrolled.
Link: http://lkml.kernel.org/r/20161229193445.1913-1-ebiggers3@gmail.com
Signed-off-by: Eric Biggers <ebiggers@google.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Lameter <cl@linux.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/buffer.c')
-rw-r--r-- | fs/buffer.c | 43 |
1 files changed, 15 insertions, 28 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index ea0e05ec2916..5715dac7821f 100644 --- a/fs/buffer.c +++ b/fs/buffer.c | |||
@@ -1281,44 +1281,31 @@ static inline void check_irqs_on(void) | |||
1281 | } | 1281 | } |
1282 | 1282 | ||
1283 | /* | 1283 | /* |
1284 | * The LRU management algorithm is dopey-but-simple. Sorry. | 1284 | * Install a buffer_head into this cpu's LRU. If not already in the LRU, it is |
1285 | * inserted at the front, and the buffer_head at the back if any is evicted. | ||
1286 | * Or, if already in the LRU it is moved to the front. | ||
1285 | */ | 1287 | */ |
1286 | static void bh_lru_install(struct buffer_head *bh) | 1288 | static void bh_lru_install(struct buffer_head *bh) |
1287 | { | 1289 | { |
1288 | struct buffer_head *evictee = NULL; | 1290 | struct buffer_head *evictee = bh; |
1291 | struct bh_lru *b; | ||
1292 | int i; | ||
1289 | 1293 | ||
1290 | check_irqs_on(); | 1294 | check_irqs_on(); |
1291 | bh_lru_lock(); | 1295 | bh_lru_lock(); |
1292 | if (__this_cpu_read(bh_lrus.bhs[0]) != bh) { | ||
1293 | struct buffer_head *bhs[BH_LRU_SIZE]; | ||
1294 | int in; | ||
1295 | int out = 0; | ||
1296 | |||
1297 | get_bh(bh); | ||
1298 | bhs[out++] = bh; | ||
1299 | for (in = 0; in < BH_LRU_SIZE; in++) { | ||
1300 | struct buffer_head *bh2 = | ||
1301 | __this_cpu_read(bh_lrus.bhs[in]); | ||
1302 | 1296 | ||
1303 | if (bh2 == bh) { | 1297 | b = this_cpu_ptr(&bh_lrus); |
1304 | __brelse(bh2); | 1298 | for (i = 0; i < BH_LRU_SIZE; i++) { |
1305 | } else { | 1299 | swap(evictee, b->bhs[i]); |
1306 | if (out >= BH_LRU_SIZE) { | 1300 | if (evictee == bh) { |
1307 | BUG_ON(evictee != NULL); | 1301 | bh_lru_unlock(); |
1308 | evictee = bh2; | 1302 | return; |
1309 | } else { | ||
1310 | bhs[out++] = bh2; | ||
1311 | } | ||
1312 | } | ||
1313 | } | 1303 | } |
1314 | while (out < BH_LRU_SIZE) | ||
1315 | bhs[out++] = NULL; | ||
1316 | memcpy(this_cpu_ptr(&bh_lrus.bhs), bhs, sizeof(bhs)); | ||
1317 | } | 1304 | } |
1318 | bh_lru_unlock(); | ||
1319 | 1305 | ||
1320 | if (evictee) | 1306 | get_bh(bh); |
1321 | __brelse(evictee); | 1307 | bh_lru_unlock(); |
1308 | brelse(evictee); | ||
1322 | } | 1309 | } |
1323 | 1310 | ||
1324 | /* | 1311 | /* |