Instead of looping over bucket lists directly, maintain bitmaps for more efficient greedy selection.
ILSVRC2012_img_train.tar (1,281,168 inodes) [1] uncompressed EROFS (vanilla): 147,059,949,568B 36m29.529s uncompressed EROFS (patched): 147,059,511,296B 1m14.920s ILSVRC2012_img_val.tar (50,001 inodes) 6,744,924,160B [2] uncompressed EROFS (vanilla): 6,713,278,464B 29.998s uncompressed EROFS (patched): 6,713,188,352B 23.753s [1] https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_train.tar $ tar -xvf ILSVRC2012_img_train.tar; rm -f ILSVRC2012_img_train.tar $ find . -name "*.tar" | while read NAME ; do tar -xvf "${NAME}"; rm -f "${NAME}"; done [2] https://image-net.org/data/ILSVRC/2012/ILSVRC2012_img_vol.tar $ mkfs.erofs --tar=f --sort none foo.erofs ILSVRC2012_img_vol.tar Signed-off-by: Gao Xiang <hsiang...@linux.alibaba.com> --- include/erofs/cache.h | 1 + lib/cache.c | 119 +++++++++++++++++++++++++++++++++--------- 2 files changed, 94 insertions(+), 26 deletions(-) diff --git a/include/erofs/cache.h b/include/erofs/cache.h index 76c5671..87d743a 100644 --- a/include/erofs/cache.h +++ b/include/erofs/cache.h @@ -61,6 +61,7 @@ struct erofs_bufmgr { /* buckets for all buffer blocks to boost up allocation */ struct list_head watermeter[META + 1][2][EROFS_MAX_BLOCK_SIZE]; + unsigned long bktmap[META + 1][2][EROFS_MAX_BLOCK_SIZE / BITS_PER_LONG]; struct erofs_buffer_block blkh; erofs_blk_t tail_blkaddr, metablkcnt; diff --git a/lib/cache.c b/lib/cache.c index 2e758ba..ca1061b 100644 --- a/lib/cache.c +++ b/lib/cache.c @@ -7,6 +7,7 @@ */ #include <stdlib.h> #include <erofs/cache.h> +#include <erofs/bitops.h> #include "erofs/print.h" static int erofs_bh_flush_drop_directly(struct erofs_buffer_head *bh) @@ -30,36 +31,72 @@ const struct erofs_bhops erofs_skip_write_bhops = { struct erofs_bufmgr *erofs_buffer_init(struct erofs_sb_info *sbi, erofs_blk_t startblk) { - struct erofs_bufmgr *bufmgr; + unsigned int blksiz = erofs_blksiz(sbi); + struct erofs_bufmgr *bmgr; int i, j, k; - bufmgr = malloc(sizeof(struct erofs_bufmgr)); - if (!bufmgr) + bmgr = malloc(sizeof(struct erofs_bufmgr)); + if (!bmgr) return NULL; - init_list_head(&bufmgr->blkh.list); - bufmgr->blkh.blkaddr = NULL_ADDR; - bufmgr->last_mapped_block = &bufmgr->blkh; - - for (i = 0; i < ARRAY_SIZE(bufmgr->watermeter); i++) - for (j = 0; j < ARRAY_SIZE(bufmgr->watermeter[0]); j++) - for (k = 0; k < (1 << sbi->blkszbits); k++) - init_list_head(&bufmgr->watermeter[i][j][k]); - bufmgr->tail_blkaddr = startblk; - bufmgr->sbi = sbi; - return bufmgr; + bmgr->sbi = sbi; + for (i = 0; i < ARRAY_SIZE(bmgr->watermeter); i++) { + for (j = 0; j < ARRAY_SIZE(bmgr->watermeter[0]); j++) { + for (k = 0; k < blksiz; k++) + init_list_head(&bmgr->watermeter[i][j][k]); + memset(bmgr->bktmap[i][j], 0, blksiz / BITS_PER_LONG); + } + } + init_list_head(&bmgr->blkh.list); + bmgr->blkh.blkaddr = NULL_ADDR; + bmgr->tail_blkaddr = startblk; + bmgr->last_mapped_block = &bmgr->blkh; + return bmgr; +} + +static void erofs_clear_bbktmap(struct erofs_bufmgr *bmgr, int type, + bool mapped, int nr) +{ + int bit = erofs_blksiz(bmgr->sbi) - (nr + 1); + + DBG_BUGON(bit < 0); + __erofs_clear_bit(bit, bmgr->bktmap[type][mapped]); } -static void erofs_update_bwatermeter(struct erofs_buffer_block *bb) +static void erofs_set_bbktmap(struct erofs_bufmgr *bmgr, int type, + bool mapped, int nr) +{ + int bit = erofs_blksiz(bmgr->sbi) - (nr + 1); + + DBG_BUGON(bit < 0); + __erofs_set_bit(bit, bmgr->bktmap[type][mapped]); +} + +static void erofs_update_bwatermeter(struct erofs_buffer_block *bb, bool free) { struct erofs_bufmgr *bmgr = bb->buffers.fsprivate; struct erofs_sb_info *sbi = bmgr->sbi; - struct list_head *bkt; - - bkt = bmgr->watermeter[bb->type][bb->blkaddr != NULL_ADDR] + - (bb->buffers.off & (erofs_blksiz(sbi) - 1)); + unsigned int blksiz = erofs_blksiz(sbi); + bool mapped = bb->blkaddr != NULL_ADDR; + struct list_head *h = bmgr->watermeter[bb->type][mapped]; + unsigned int nr; + + if (bb->sibling.next == bb->sibling.prev) { + if ((uintptr_t)(bb->sibling.next - h) < blksiz) { + nr = bb->sibling.next - h; + erofs_clear_bbktmap(bmgr, bb->type, mapped, nr); + } else if (bb->sibling.next != &bb->sibling) { + nr = bb->sibling.next - + bmgr->watermeter[bb->type][!mapped]; + erofs_clear_bbktmap(bmgr, bb->type, !mapped, nr); + } + } list_del(&bb->sibling); - list_add_tail(&bb->sibling, bkt); + if (free) + return; + nr = bb->buffers.off & (blksiz - 1); + list_add_tail(&bb->sibling, h + nr); + erofs_set_bbktmap(bmgr, bb->type, mapped, nr); } /* return occupied bytes in specific buffer block if succeed */ @@ -114,7 +151,7 @@ static int __erofs_battach(struct erofs_buffer_block *bb, /* need to update the tail_blkaddr */ if (tailupdate) bmgr->tail_blkaddr = blkaddr + bb->buffers.nblocks; - erofs_update_bwatermeter(bb); + erofs_update_bwatermeter(bb, false); } return ((alignedoffset + incr + blkmask) & blkmask) + 1; } @@ -130,6 +167,36 @@ int erofs_bh_balloon(struct erofs_buffer_head *bh, erofs_off_t incr) return __erofs_battach(bb, NULL, incr, 1, 0, false); } +static bool __find_next_bucket(struct erofs_bufmgr *bmgr, int type, bool mapped, + unsigned int *index, unsigned int end) +{ + const unsigned int blksiz = erofs_blksiz(bmgr->sbi); + const unsigned int blkmask = blksiz - 1; + unsigned int l = *index, r; + + if (l <= end) { + DBG_BUGON(l < end); + return false; + } + + l = blkmask - (l & blkmask); + r = blkmask - (end & blkmask); + if (l >= r) { + l = erofs_find_next_bit(bmgr->bktmap[type][mapped], blksiz, l); + if (l < blksiz) { + *index = round_down(*index, blksiz) + blkmask - l; + return true; + } + l = 0; + *index -= blksiz; + } + l = erofs_find_next_bit(bmgr->bktmap[type][mapped], r, l); + if (l >= r) + return false; + *index = round_down(*index, blksiz) + blkmask - l; + return true; +} + static int erofs_bfind_for_attach(struct erofs_bufmgr *bmgr, int type, erofs_off_t size, unsigned int inline_ext, @@ -163,13 +230,13 @@ static int erofs_bfind_for_attach(struct erofs_bufmgr *bmgr, while (mapped) { --mapped; - for (; index > end; --index) { + for (; __find_next_bucket(bmgr, type, mapped, &index, end); + --index) { struct list_head *bt; used = index & blkmask; bt = bmgr->watermeter[type][mapped] + used; - if (list_empty(bt)) - continue; + DBG_BUGON(list_empty(bt)); cur = list_first_entry(bt, struct erofs_buffer_block, sibling); @@ -332,7 +399,7 @@ static void __erofs_mapbh(struct erofs_buffer_block *bb) } } bmgr->last_mapped_block = bb; - erofs_update_bwatermeter(bb); + erofs_update_bwatermeter(bb, false); } blkaddr = bb->blkaddr + bb->buffers.nblocks; @@ -371,7 +438,7 @@ static void erofs_bfree(struct erofs_buffer_block *bb) if (bb == bmgr->last_mapped_block) bmgr->last_mapped_block = list_prev_entry(bb, list); - list_del(&bb->sibling); + erofs_update_bwatermeter(bb, true); list_del(&bb->list); free(bb); } -- 2.43.5