All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/2] erofs-utils: optimize mkfs to O(N), N = #files
@ 2021-01-01  9:11 胡玮文
  2021-01-01  9:50 ` 胡 玮文
  2021-01-08 18:15 ` Gao Xiang
  0 siblings, 2 replies; 7+ messages in thread
From: 胡玮文 @ 2021-01-01  9:11 UTC (permalink / raw)
  To: Li Guifu, Miao Xie, Fang Wei
  Cc: linux-erofs, Gao Xiang, 胡玮文

When using EROFS to pack our dataset which consists of millions of
files, mkfs.erofs is very slow compared with mksquashfs.

The bottleneck is `erofs_balloc` and `erofs_mapbh` function, which
iterate over all previously allocated buffer blocks, making the
complexity of the algrithm O(N^2) where N is the number of files.

With this patch:

* global `last_mapped_block` is mantained to avoid full scan in
  `erofs_mapbh` function.

* global `non_full_buffer_blocks` mantains a list of buffer block for
  each type and each possible remaining bytes in the block. Then it is
  used to identify the most suitable blocks in future `erofs_balloc`,
  avoiding full scan.

Some new data structure is allocated in this patch, more RAM usage is
expected, but not much. When I test it with ImageNet dataset (1.33M
files), 7GiB RAM is consumed, and it takes about 4 hours. Most time is
spent on IO.

Signed-off-by: Hu Weiwen <huww98@outlook.com>
---
 lib/cache.c | 102 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 89 insertions(+), 13 deletions(-)

diff --git a/lib/cache.c b/lib/cache.c
index 0d5c4a5..3412a0b 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -18,6 +18,18 @@ static struct erofs_buffer_block blkh = {
 };
 static erofs_blk_t tail_blkaddr;
 
+/**
+ * Some buffers are not full, we can reuse it to store more data
+ * 2 for DATA, META
+ * EROFS_BLKSIZ for each possible remaining bytes in the last block
+ **/
+static struct erofs_buffer_block_record {
+	struct list_head list;
+	struct erofs_buffer_block *bb;
+} non_full_buffer_blocks[2][EROFS_BLKSIZ];
+
+static struct erofs_buffer_block *last_mapped_block = &blkh;
+
 static bool erofs_bh_flush_drop_directly(struct erofs_buffer_head *bh)
 {
 	return erofs_bh_flush_generic_end(bh);
@@ -62,6 +74,12 @@ struct erofs_bhops erofs_buf_write_bhops = {
 /* return buffer_head of erofs super block (with size 0) */
 struct erofs_buffer_head *erofs_buffer_init(void)
 {
+	for (int i = 0; i < 2; i++) {
+		for (int j = 0; j < EROFS_BLKSIZ; j++) {
+			init_list_head(&non_full_buffer_blocks[i][j].list);
+		}
+	}
+
 	struct erofs_buffer_head *bh = erofs_balloc(META, 0, 0, 0);
 
 	if (IS_ERR(bh))
@@ -131,7 +149,8 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
 {
 	struct erofs_buffer_block *cur, *bb;
 	struct erofs_buffer_head *bh;
-	unsigned int alignsize, used0, usedmax;
+	unsigned int alignsize, used0, usedmax, total_size;
+	struct erofs_buffer_block_record * reusing = NULL;
 
 	int ret = get_alignsize(type, &type);
 
@@ -143,7 +162,38 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
 	usedmax = 0;
 	bb = NULL;
 
-	list_for_each_entry(cur, &blkh.list, list) {
+	erofs_dbg("balloc size=%lu alignsize=%u used0=%u", size, alignsize, used0);
+	if (used0 == 0 || alignsize == EROFS_BLKSIZ) {
+		goto alloc;
+	}
+	total_size = size + required_ext + inline_ext;
+	if (total_size < EROFS_BLKSIZ) {
+		// Try find a most fit block.
+		DBG_BUGON(type < 0 || type > 1);
+		struct erofs_buffer_block_record *non_fulls = non_full_buffer_blocks[type];
+		for (struct erofs_buffer_block_record *r = non_fulls + round_up(total_size, alignsize);
+				r < non_fulls + EROFS_BLKSIZ; r++) {
+			if (!list_empty(&r->list)) {
+				struct erofs_buffer_block_record *reuse_candidate =
+						list_first_entry(&r->list, struct erofs_buffer_block_record, list);
+				ret = __erofs_battach(reuse_candidate->bb, NULL, size, alignsize,
+						required_ext + inline_ext, true);
+				if (ret >= 0) {
+					reusing = reuse_candidate;
+					bb = reuse_candidate->bb;
+					usedmax = (ret + required_ext) % EROFS_BLKSIZ + inline_ext;
+				}
+				break;
+			}
+		}
+	}
+
+	/* Try reuse last ones, which are not mapped and can be extended */
+	cur = last_mapped_block;
+	if (cur == &blkh) {
+		cur = list_next_entry(cur, list);
+	}
+	for (; cur != &blkh; cur = list_next_entry(cur, list)) {
 		unsigned int used_before, used;
 
 		used_before = cur->buffers.off % EROFS_BLKSIZ;
@@ -175,22 +225,32 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
 			continue;
 
 		if (usedmax < used) {
+			reusing = NULL;
 			bb = cur;
 			usedmax = used;
 		}
 	}
 
 	if (bb) {
+		erofs_dbg("reusing buffer. usedmax=%u", usedmax);
 		bh = malloc(sizeof(struct erofs_buffer_head));
 		if (!bh)
 			return ERR_PTR(-ENOMEM);
+		if (reusing) {
+			list_del(&reusing->list);
+			free(reusing);
+		}
 		goto found;
 	}
 
+alloc:
 	/* allocate a new buffer block */
-	if (used0 > EROFS_BLKSIZ)
+	if (used0 > EROFS_BLKSIZ) {
+		erofs_dbg("used0 > EROFS_BLKSIZ");
 		return ERR_PTR(-ENOSPC);
+	}
 
+	erofs_dbg("new buffer block");
 	bb = malloc(sizeof(struct erofs_buffer_block));
 	if (!bb)
 		return ERR_PTR(-ENOMEM);
@@ -211,6 +271,16 @@ found:
 			      required_ext + inline_ext, false);
 	if (ret < 0)
 		return ERR_PTR(ret);
+	if (ret != 0) {
+		/* This buffer is not full yet, we may reuse it */
+		reusing = malloc(sizeof(struct erofs_buffer_block_record));
+		if (!reusing) {
+			return ERR_PTR(-ENOMEM);
+		}
+		reusing->bb = bb;
+		list_add_tail(&reusing->list,
+				&non_full_buffer_blocks[type][EROFS_BLKSIZ - ret - inline_ext].list);
+	}
 	return bh;
 }
 
@@ -247,8 +317,10 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
 {
 	erofs_blk_t blkaddr;
 
-	if (bb->blkaddr == NULL_ADDR)
+	if (bb->blkaddr == NULL_ADDR) {
 		bb->blkaddr = tail_blkaddr;
+		last_mapped_block = bb;
+	}
 
 	blkaddr = bb->blkaddr + BLK_ROUND_UP(bb->buffers.off);
 	if (blkaddr > tail_blkaddr)
@@ -259,15 +331,16 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
 
 erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb, bool end)
 {
-	struct erofs_buffer_block *t, *nt;
-
-	if (!bb || bb->blkaddr == NULL_ADDR) {
-		list_for_each_entry_safe(t, nt, &blkh.list, list) {
-			if (!end && (t == bb || nt == &blkh))
-				break;
-			(void)__erofs_mapbh(t);
-			if (end && t == bb)
-				break;
+	DBG_BUGON(!end);
+	struct erofs_buffer_block *t = last_mapped_block;
+	while (1) {
+		t = list_next_entry(t, list);
+		if (t == &blkh) {
+			break;
+		}
+		(void)__erofs_mapbh(t);
+		if (t == bb) {
+			break;
 		}
 	}
 	return tail_blkaddr;
@@ -334,6 +407,9 @@ void erofs_bdrop(struct erofs_buffer_head *bh, bool tryrevoke)
 	if (!list_empty(&bb->buffers.list))
 		return;
 
+	if (bb == last_mapped_block) {
+		last_mapped_block = list_prev_entry(bb, list);
+	}
 	list_del(&bb->list);
 	free(bb);
 
-- 
2.30.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] erofs-utils: optimize mkfs to O(N), N = #files
  2021-01-01  9:11 [PATCH 1/2] erofs-utils: optimize mkfs to O(N), N = #files 胡玮文
@ 2021-01-01  9:50 ` 胡 玮文
  2021-01-02  5:09   ` Gao Xiang
  2021-01-08 18:15 ` Gao Xiang
  1 sibling, 1 reply; 7+ messages in thread
From: 胡 玮文 @ 2021-01-01  9:50 UTC (permalink / raw)
  To: linux-erofs

[-- Attachment #1: Type: text/plain, Size: 9279 bytes --]

Hi all,

This is my first time sending patches by email. If something is not properly done, please point it out.

I have a question for now: How am I supposed to submit patches to erofs-utils now? Some E-mail address listed in AUTHORS and README seems no longer valid (email to gaoxiang25@huawei.com<mailto:gaoxiang25@huawei.com> was rejected). Is it enough to post the patches to this mailing list? I added all addresses in README, but I got a message said my email is rejected. I’m not sure who actually received my email.

发件人: 胡 玮文<mailto:huww98@outlook.com>
发送时间: 2021年1月1日 17:16
收件人: Li Guifu<mailto:bluce.liguifu@huawei.com>; Miao Xie<mailto:miaoxie@huawei.com>; Fang Wei<mailto:fangwei1@huawei.com>
抄送: Gao Xiang<mailto:gaoxiang25@huawei.com>; Chao Yu<mailto:yuchao0@huawei.com>; linux-erofs@lists.ozlabs.org<mailto:linux-erofs@lists.ozlabs.org>; 胡玮文<mailto:huww98@outlook.com>
主题: [PATCH 1/2] erofs-utils: optimize mkfs to O(N), N = #files

When using EROFS to pack our dataset which consists of millions of
files, mkfs.erofs is very slow compared with mksquashfs.

The bottleneck is `erofs_balloc` and `erofs_mapbh` function, which
iterate over all previously allocated buffer blocks, making the
complexity of the algrithm O(N^2) where N is the number of files.

With this patch:

* global `last_mapped_block` is mantained to avoid full scan in
  `erofs_mapbh` function.

* global `non_full_buffer_blocks` mantains a list of buffer block for
  each type and each possible remaining bytes in the block. Then it is
  used to identify the most suitable blocks in future `erofs_balloc`,
  avoiding full scan.

Some new data structure is allocated in this patch, more RAM usage is
expected, but not much. When I test it with ImageNet dataset (1.33M
files), 7GiB RAM is consumed, and it takes about 4 hours. Most time is
spent on IO.

Signed-off-by: Hu Weiwen <huww98@outlook.com>
---
 lib/cache.c | 102 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 89 insertions(+), 13 deletions(-)

diff --git a/lib/cache.c b/lib/cache.c
index 0d5c4a5..3412a0b 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -18,6 +18,18 @@ static struct erofs_buffer_block blkh = {
 };
 static erofs_blk_t tail_blkaddr;

+/**
+ * Some buffers are not full, we can reuse it to store more data
+ * 2 for DATA, META
+ * EROFS_BLKSIZ for each possible remaining bytes in the last block
+ **/
+static struct erofs_buffer_block_record {
+       struct list_head list;
+       struct erofs_buffer_block *bb;
+} non_full_buffer_blocks[2][EROFS_BLKSIZ];
+
+static struct erofs_buffer_block *last_mapped_block = &blkh;
+
 static bool erofs_bh_flush_drop_directly(struct erofs_buffer_head *bh)
 {
         return erofs_bh_flush_generic_end(bh);
@@ -62,6 +74,12 @@ struct erofs_bhops erofs_buf_write_bhops = {
 /* return buffer_head of erofs super block (with size 0) */
 struct erofs_buffer_head *erofs_buffer_init(void)
 {
+       for (int i = 0; i < 2; i++) {
+               for (int j = 0; j < EROFS_BLKSIZ; j++) {
+                       init_list_head(&non_full_buffer_blocks[i][j].list);
+               }
+       }
+
         struct erofs_buffer_head *bh = erofs_balloc(META, 0, 0, 0);

         if (IS_ERR(bh))
@@ -131,7 +149,8 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
 {
         struct erofs_buffer_block *cur, *bb;
         struct erofs_buffer_head *bh;
-       unsigned int alignsize, used0, usedmax;
+       unsigned int alignsize, used0, usedmax, total_size;
+       struct erofs_buffer_block_record * reusing = NULL;

         int ret = get_alignsize(type, &type);

@@ -143,7 +162,38 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
         usedmax = 0;
         bb = NULL;

-       list_for_each_entry(cur, &blkh.list, list) {
+       erofs_dbg("balloc size=%lu alignsize=%u used0=%u", size, alignsize, used0);
+       if (used0 == 0 || alignsize == EROFS_BLKSIZ) {
+               goto alloc;
+       }
+       total_size = size + required_ext + inline_ext;
+       if (total_size < EROFS_BLKSIZ) {
+               // Try find a most fit block.
+               DBG_BUGON(type < 0 || type > 1);
+               struct erofs_buffer_block_record *non_fulls = non_full_buffer_blocks[type];
+               for (struct erofs_buffer_block_record *r = non_fulls + round_up(total_size, alignsize);
+                               r < non_fulls + EROFS_BLKSIZ; r++) {
+                       if (!list_empty(&r->list)) {
+                               struct erofs_buffer_block_record *reuse_candidate =
+                                               list_first_entry(&r->list, struct erofs_buffer_block_record, list);
+                               ret = __erofs_battach(reuse_candidate->bb, NULL, size, alignsize,
+                                               required_ext + inline_ext, true);
+                               if (ret >= 0) {
+                                       reusing = reuse_candidate;
+                                       bb = reuse_candidate->bb;
+                                       usedmax = (ret + required_ext) % EROFS_BLKSIZ + inline_ext;
+                               }
+                               break;
+                       }
+               }
+       }
+
+       /* Try reuse last ones, which are not mapped and can be extended */
+       cur = last_mapped_block;
+       if (cur == &blkh) {
+               cur = list_next_entry(cur, list);
+       }
+       for (; cur != &blkh; cur = list_next_entry(cur, list)) {
                 unsigned int used_before, used;

                 used_before = cur->buffers.off % EROFS_BLKSIZ;
@@ -175,22 +225,32 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
                         continue;

                 if (usedmax < used) {
+                       reusing = NULL;
                         bb = cur;
                         usedmax = used;
                 }
         }

         if (bb) {
+               erofs_dbg("reusing buffer. usedmax=%u", usedmax);
                 bh = malloc(sizeof(struct erofs_buffer_head));
                 if (!bh)
                         return ERR_PTR(-ENOMEM);
+               if (reusing) {
+                       list_del(&reusing->list);
+                       free(reusing);
+               }
                 goto found;
         }

+alloc:
         /* allocate a new buffer block */
-       if (used0 > EROFS_BLKSIZ)
+       if (used0 > EROFS_BLKSIZ) {
+               erofs_dbg("used0 > EROFS_BLKSIZ");
                 return ERR_PTR(-ENOSPC);
+       }

+       erofs_dbg("new buffer block");
         bb = malloc(sizeof(struct erofs_buffer_block));
         if (!bb)
                 return ERR_PTR(-ENOMEM);
@@ -211,6 +271,16 @@ found:
                               required_ext + inline_ext, false);
         if (ret < 0)
                 return ERR_PTR(ret);
+       if (ret != 0) {
+               /* This buffer is not full yet, we may reuse it */
+               reusing = malloc(sizeof(struct erofs_buffer_block_record));
+               if (!reusing) {
+                       return ERR_PTR(-ENOMEM);
+               }
+               reusing->bb = bb;
+               list_add_tail(&reusing->list,
+                               &non_full_buffer_blocks[type][EROFS_BLKSIZ - ret - inline_ext].list);
+       }
         return bh;
 }

@@ -247,8 +317,10 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
 {
         erofs_blk_t blkaddr;

-       if (bb->blkaddr == NULL_ADDR)
+       if (bb->blkaddr == NULL_ADDR) {
                 bb->blkaddr = tail_blkaddr;
+               last_mapped_block = bb;
+       }

         blkaddr = bb->blkaddr + BLK_ROUND_UP(bb->buffers.off);
         if (blkaddr > tail_blkaddr)
@@ -259,15 +331,16 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)

 erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb, bool end)
 {
-       struct erofs_buffer_block *t, *nt;
-
-       if (!bb || bb->blkaddr == NULL_ADDR) {
-               list_for_each_entry_safe(t, nt, &blkh.list, list) {
-                       if (!end && (t == bb || nt == &blkh))
-                               break;
-                       (void)__erofs_mapbh(t);
-                       if (end && t == bb)
-                               break;
+       DBG_BUGON(!end);
+       struct erofs_buffer_block *t = last_mapped_block;
+       while (1) {
+               t = list_next_entry(t, list);
+               if (t == &blkh) {
+                       break;
+               }
+               (void)__erofs_mapbh(t);
+               if (t == bb) {
+                       break;
                 }
         }
         return tail_blkaddr;
@@ -334,6 +407,9 @@ void erofs_bdrop(struct erofs_buffer_head *bh, bool tryrevoke)
         if (!list_empty(&bb->buffers.list))
                 return;

+       if (bb == last_mapped_block) {
+               last_mapped_block = list_prev_entry(bb, list);
+       }
         list_del(&bb->list);
         free(bb);

--
2.30.0


[-- Attachment #2: Type: text/html, Size: 22815 bytes --]

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] erofs-utils: optimize mkfs to O(N), N = #files
  2021-01-01  9:50 ` 胡 玮文
@ 2021-01-02  5:09   ` Gao Xiang
  0 siblings, 0 replies; 7+ messages in thread
From: Gao Xiang @ 2021-01-02  5:09 UTC (permalink / raw)
  To: 胡 玮文; +Cc: linux-erofs

Hi Weiwen,

On Fri, Jan 01, 2021 at 09:50:24AM +0000, 胡 玮文 wrote:
> Hi all,
> 
> This is my first time sending patches by email. If something is not properly done, please point it out.
> 
> I have a question for now: How am I supposed to submit patches to erofs-utils now? Some E-mail address listed in AUTHORS and README seems no longer valid (email to gaoxiang25@huawei.com<mailto:gaoxiang25@huawei.com> was rejected). Is it enough to post the patches to this mailing list? I added all addresses in README, but I got a message said my email is rejected. I’m not sure who actually received my email.

My email has been changed, I might need to update my email address to hsiangkao@redhat.com...
Thanks for point out!

Yeah, that could cause bottleneck when files increased (Android only have < 10000 files),
so it needs to be improve. I will look into your patch later (working on another things now...)
Hope Guifu or other folks could also help reviewing that...

Thanks,
Gao Xiang

> 
> 发件人: 胡 玮文<mailto:huww98@outlook.com>
> 发送时间: 2021年1月1日 17:16
> 收件人: Li Guifu<mailto:bluce.liguifu@huawei.com>; Miao Xie<mailto:miaoxie@huawei.com>; Fang Wei<mailto:fangwei1@huawei.com>
> 抄送: Gao Xiang<mailto:gaoxiang25@huawei.com>; Chao Yu<mailto:yuchao0@huawei.com>; linux-erofs@lists.ozlabs.org<mailto:linux-erofs@lists.ozlabs.org>; 胡玮文<mailto:huww98@outlook.com>
> 主题: [PATCH 1/2] erofs-utils: optimize mkfs to O(N), N = #files
> 
> When using EROFS to pack our dataset which consists of millions of
> files, mkfs.erofs is very slow compared with mksquashfs.
> 
> The bottleneck is `erofs_balloc` and `erofs_mapbh` function, which
> iterate over all previously allocated buffer blocks, making the
> complexity of the algrithm O(N^2) where N is the number of files.
> 
> With this patch:
> 
> * global `last_mapped_block` is mantained to avoid full scan in
>   `erofs_mapbh` function.
> 
> * global `non_full_buffer_blocks` mantains a list of buffer block for
>   each type and each possible remaining bytes in the block. Then it is
>   used to identify the most suitable blocks in future `erofs_balloc`,
>   avoiding full scan.
> 
> Some new data structure is allocated in this patch, more RAM usage is
> expected, but not much. When I test it with ImageNet dataset (1.33M
> files), 7GiB RAM is consumed, and it takes about 4 hours. Most time is
> spent on IO.
> 
> Signed-off-by: Hu Weiwen <huww98@outlook.com>
> ---
>  lib/cache.c | 102 +++++++++++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 89 insertions(+), 13 deletions(-)
> 
> diff --git a/lib/cache.c b/lib/cache.c
> index 0d5c4a5..3412a0b 100644
> --- a/lib/cache.c
> +++ b/lib/cache.c
> @@ -18,6 +18,18 @@ static struct erofs_buffer_block blkh = {
>  };
>  static erofs_blk_t tail_blkaddr;
> 
> +/**
> + * Some buffers are not full, we can reuse it to store more data
> + * 2 for DATA, META
> + * EROFS_BLKSIZ for each possible remaining bytes in the last block
> + **/
> +static struct erofs_buffer_block_record {
> +       struct list_head list;
> +       struct erofs_buffer_block *bb;
> +} non_full_buffer_blocks[2][EROFS_BLKSIZ];
> +
> +static struct erofs_buffer_block *last_mapped_block = &blkh;
> +
>  static bool erofs_bh_flush_drop_directly(struct erofs_buffer_head *bh)
>  {
>          return erofs_bh_flush_generic_end(bh);
> @@ -62,6 +74,12 @@ struct erofs_bhops erofs_buf_write_bhops = {
>  /* return buffer_head of erofs super block (with size 0) */
>  struct erofs_buffer_head *erofs_buffer_init(void)
>  {
> +       for (int i = 0; i < 2; i++) {
> +               for (int j = 0; j < EROFS_BLKSIZ; j++) {
> +                       init_list_head(&non_full_buffer_blocks[i][j].list);
> +               }
> +       }
> +
>          struct erofs_buffer_head *bh = erofs_balloc(META, 0, 0, 0);
> 
>          if (IS_ERR(bh))
> @@ -131,7 +149,8 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
>  {
>          struct erofs_buffer_block *cur, *bb;
>          struct erofs_buffer_head *bh;
> -       unsigned int alignsize, used0, usedmax;
> +       unsigned int alignsize, used0, usedmax, total_size;
> +       struct erofs_buffer_block_record * reusing = NULL;
> 
>          int ret = get_alignsize(type, &type);
> 
> @@ -143,7 +162,38 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
>          usedmax = 0;
>          bb = NULL;
> 
> -       list_for_each_entry(cur, &blkh.list, list) {
> +       erofs_dbg("balloc size=%lu alignsize=%u used0=%u", size, alignsize, used0);
> +       if (used0 == 0 || alignsize == EROFS_BLKSIZ) {
> +               goto alloc;
> +       }
> +       total_size = size + required_ext + inline_ext;
> +       if (total_size < EROFS_BLKSIZ) {
> +               // Try find a most fit block.
> +               DBG_BUGON(type < 0 || type > 1);
> +               struct erofs_buffer_block_record *non_fulls = non_full_buffer_blocks[type];
> +               for (struct erofs_buffer_block_record *r = non_fulls + round_up(total_size, alignsize);
> +                               r < non_fulls + EROFS_BLKSIZ; r++) {
> +                       if (!list_empty(&r->list)) {
> +                               struct erofs_buffer_block_record *reuse_candidate =
> +                                               list_first_entry(&r->list, struct erofs_buffer_block_record, list);
> +                               ret = __erofs_battach(reuse_candidate->bb, NULL, size, alignsize,
> +                                               required_ext + inline_ext, true);
> +                               if (ret >= 0) {
> +                                       reusing = reuse_candidate;
> +                                       bb = reuse_candidate->bb;
> +                                       usedmax = (ret + required_ext) % EROFS_BLKSIZ + inline_ext;
> +                               }
> +                               break;
> +                       }
> +               }
> +       }
> +
> +       /* Try reuse last ones, which are not mapped and can be extended */
> +       cur = last_mapped_block;
> +       if (cur == &blkh) {
> +               cur = list_next_entry(cur, list);
> +       }
> +       for (; cur != &blkh; cur = list_next_entry(cur, list)) {
>                  unsigned int used_before, used;
> 
>                  used_before = cur->buffers.off % EROFS_BLKSIZ;
> @@ -175,22 +225,32 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
>                          continue;
> 
>                  if (usedmax < used) {
> +                       reusing = NULL;
>                          bb = cur;
>                          usedmax = used;
>                  }
>          }
> 
>          if (bb) {
> +               erofs_dbg("reusing buffer. usedmax=%u", usedmax);
>                  bh = malloc(sizeof(struct erofs_buffer_head));
>                  if (!bh)
>                          return ERR_PTR(-ENOMEM);
> +               if (reusing) {
> +                       list_del(&reusing->list);
> +                       free(reusing);
> +               }
>                  goto found;
>          }
> 
> +alloc:
>          /* allocate a new buffer block */
> -       if (used0 > EROFS_BLKSIZ)
> +       if (used0 > EROFS_BLKSIZ) {
> +               erofs_dbg("used0 > EROFS_BLKSIZ");
>                  return ERR_PTR(-ENOSPC);
> +       }
> 
> +       erofs_dbg("new buffer block");
>          bb = malloc(sizeof(struct erofs_buffer_block));
>          if (!bb)
>                  return ERR_PTR(-ENOMEM);
> @@ -211,6 +271,16 @@ found:
>                                required_ext + inline_ext, false);
>          if (ret < 0)
>                  return ERR_PTR(ret);
> +       if (ret != 0) {
> +               /* This buffer is not full yet, we may reuse it */
> +               reusing = malloc(sizeof(struct erofs_buffer_block_record));
> +               if (!reusing) {
> +                       return ERR_PTR(-ENOMEM);
> +               }
> +               reusing->bb = bb;
> +               list_add_tail(&reusing->list,
> +                               &non_full_buffer_blocks[type][EROFS_BLKSIZ - ret - inline_ext].list);
> +       }
>          return bh;
>  }
> 
> @@ -247,8 +317,10 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
>  {
>          erofs_blk_t blkaddr;
> 
> -       if (bb->blkaddr == NULL_ADDR)
> +       if (bb->blkaddr == NULL_ADDR) {
>                  bb->blkaddr = tail_blkaddr;
> +               last_mapped_block = bb;
> +       }
> 
>          blkaddr = bb->blkaddr + BLK_ROUND_UP(bb->buffers.off);
>          if (blkaddr > tail_blkaddr)
> @@ -259,15 +331,16 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
> 
>  erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb, bool end)
>  {
> -       struct erofs_buffer_block *t, *nt;
> -
> -       if (!bb || bb->blkaddr == NULL_ADDR) {
> -               list_for_each_entry_safe(t, nt, &blkh.list, list) {
> -                       if (!end && (t == bb || nt == &blkh))
> -                               break;
> -                       (void)__erofs_mapbh(t);
> -                       if (end && t == bb)
> -                               break;
> +       DBG_BUGON(!end);
> +       struct erofs_buffer_block *t = last_mapped_block;
> +       while (1) {
> +               t = list_next_entry(t, list);
> +               if (t == &blkh) {
> +                       break;
> +               }
> +               (void)__erofs_mapbh(t);
> +               if (t == bb) {
> +                       break;
>                  }
>          }
>          return tail_blkaddr;
> @@ -334,6 +407,9 @@ void erofs_bdrop(struct erofs_buffer_head *bh, bool tryrevoke)
>          if (!list_empty(&bb->buffers.list))
>                  return;
> 
> +       if (bb == last_mapped_block) {
> +               last_mapped_block = list_prev_entry(bb, list);
> +       }
>          list_del(&bb->list);
>          free(bb);
> 
> --
> 2.30.0
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/2] erofs-utils: optimize mkfs to O(N), N = #files
  2021-01-01  9:11 [PATCH 1/2] erofs-utils: optimize mkfs to O(N), N = #files 胡玮文
  2021-01-01  9:50 ` 胡 玮文
@ 2021-01-08 18:15 ` Gao Xiang
  2021-01-09  8:28   ` [PATCH v2 0/2] Optimize " Hu Weiwen
  1 sibling, 1 reply; 7+ messages in thread
From: Gao Xiang @ 2021-01-08 18:15 UTC (permalink / raw)
  To: 胡玮文; +Cc: Gao Xiang, linux-erofs, Miao Xie

Hi Weiwen,

On Fri, Jan 01, 2021 at 05:11:57PM +0800, 胡玮文 wrote:
> When using EROFS to pack our dataset which consists of millions of
> files, mkfs.erofs is very slow compared with mksquashfs.
> 
> The bottleneck is `erofs_balloc` and `erofs_mapbh` function, which
> iterate over all previously allocated buffer blocks, making the
> complexity of the algrithm O(N^2) where N is the number of files.
> 
> With this patch:
> 
> * global `last_mapped_block` is mantained to avoid full scan in
>   `erofs_mapbh` function.
> 
> * global `non_full_buffer_blocks` mantains a list of buffer block for
>   each type and each possible remaining bytes in the block. Then it is
>   used to identify the most suitable blocks in future `erofs_balloc`,
>   avoiding full scan.
> 
> Some new data structure is allocated in this patch, more RAM usage is
> expected, but not much. When I test it with ImageNet dataset (1.33M
> files), 7GiB RAM is consumed, and it takes about 4 hours. Most time is
> spent on IO.

Thanks for your patch again. I've read your patch, sorry about the delay
due to my pending work.

The idea generally looks to me.
Some suggestion about this as follows... :)

> 
> Signed-off-by: Hu Weiwen <huww98@outlook.com>
> ---
>  lib/cache.c | 102 +++++++++++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 89 insertions(+), 13 deletions(-)
> 
> diff --git a/lib/cache.c b/lib/cache.c
> index 0d5c4a5..3412a0b 100644
> --- a/lib/cache.c
> +++ b/lib/cache.c
> @@ -18,6 +18,18 @@ static struct erofs_buffer_block blkh = {
>  };
>  static erofs_blk_t tail_blkaddr;
>  
> +/**
> + * Some buffers are not full, we can reuse it to store more data
> + * 2 for DATA, META
> + * EROFS_BLKSIZ for each possible remaining bytes in the last block
> + **/
> +static struct erofs_buffer_block_record {
> +	struct list_head list;
> +	struct erofs_buffer_block *bb;
> +} non_full_buffer_blocks[2][EROFS_BLKSIZ];
> +

How about integrating the list_head to struct erofs_buffer_block and therefore
no need to malloc(struct erofs_buffer_block_record)?

and then we have
static struct list_head nonfull_bb_buckets[2][EROFS_BLKSIZ];

> +static struct erofs_buffer_block *last_mapped_block = &blkh;
> +
>  static bool erofs_bh_flush_drop_directly(struct erofs_buffer_head *bh)
>  {
>  	return erofs_bh_flush_generic_end(bh);
> @@ -62,6 +74,12 @@ struct erofs_bhops erofs_buf_write_bhops = {
>  /* return buffer_head of erofs super block (with size 0) */
>  struct erofs_buffer_head *erofs_buffer_init(void)
>  {
> +	for (int i = 0; i < 2; i++) {
> +		for (int j = 0; j < EROFS_BLKSIZ; j++) {
> +			init_list_head(&non_full_buffer_blocks[i][j].list);
> +		}
> +	}
> +

erofs-utils follows kernel coding style. so for the single statement,
no need to introduce braces...

https://www.kernel.org/doc/html/latest/process/coding-style.html

>  	struct erofs_buffer_head *bh = erofs_balloc(META, 0, 0, 0);
>  
>  	if (IS_ERR(bh))
> @@ -131,7 +149,8 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
>  {
>  	struct erofs_buffer_block *cur, *bb;
>  	struct erofs_buffer_head *bh;
> -	unsigned int alignsize, used0, usedmax;
> +	unsigned int alignsize, used0, usedmax, total_size;
> +	struct erofs_buffer_block_record * reusing = NULL;
>  
>  	int ret = get_alignsize(type, &type);
>  
> @@ -143,7 +162,38 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
>  	usedmax = 0;
>  	bb = NULL;
>  
> -	list_for_each_entry(cur, &blkh.list, list) {
> +	erofs_dbg("balloc size=%lu alignsize=%u used0=%u", size, alignsize, used0);
> +	if (used0 == 0 || alignsize == EROFS_BLKSIZ) {
> +		goto alloc;
> +	}

same here.

> +	total_size = size + required_ext + inline_ext;
> +	if (total_size < EROFS_BLKSIZ) {
> +		// Try find a most fit block.
> +		DBG_BUGON(type < 0 || type > 1);
> +		struct erofs_buffer_block_record *non_fulls = non_full_buffer_blocks[type];
> +		for (struct erofs_buffer_block_record *r = non_fulls + round_up(total_size, alignsize);
> +				r < non_fulls + EROFS_BLKSIZ; r++) {
> +			if (!list_empty(&r->list)) {
> +				struct erofs_buffer_block_record *reuse_candidate =
> +						list_first_entry(&r->list, struct erofs_buffer_block_record, list);
> +				ret = __erofs_battach(reuse_candidate->bb, NULL, size, alignsize,
> +						required_ext + inline_ext, true);
> +				if (ret >= 0) {
> +					reusing = reuse_candidate;
> +					bb = reuse_candidate->bb;
> +					usedmax = (ret + required_ext) % EROFS_BLKSIZ + inline_ext;
> +				}
> +				break;
> +			}
> +		}
> +	}
> +
> +	/* Try reuse last ones, which are not mapped and can be extended */
> +	cur = last_mapped_block;
> +	if (cur == &blkh) {
> +		cur = list_next_entry(cur, list);
> +	}

same here. 

> +	for (; cur != &blkh; cur = list_next_entry(cur, list)) {
>  		unsigned int used_before, used;
>  
>  		used_before = cur->buffers.off % EROFS_BLKSIZ;
> @@ -175,22 +225,32 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
>  			continue;
>  
>  		if (usedmax < used) {
> +			reusing = NULL;
>  			bb = cur;
>  			usedmax = used;
>  		}
>  	}
>  
>  	if (bb) {
> +		erofs_dbg("reusing buffer. usedmax=%u", usedmax);
>  		bh = malloc(sizeof(struct erofs_buffer_head));
>  		if (!bh)
>  			return ERR_PTR(-ENOMEM);
> +		if (reusing) {
> +			list_del(&reusing->list);
> +			free(reusing);
> +		}
>  		goto found;
>  	}
>  
> +alloc:
>  	/* allocate a new buffer block */
> -	if (used0 > EROFS_BLKSIZ)
> +	if (used0 > EROFS_BLKSIZ) {
> +		erofs_dbg("used0 > EROFS_BLKSIZ");
>  		return ERR_PTR(-ENOSPC);
> +	}
>  
> +	erofs_dbg("new buffer block");
>  	bb = malloc(sizeof(struct erofs_buffer_block));
>  	if (!bb)
>  		return ERR_PTR(-ENOMEM);
> @@ -211,6 +271,16 @@ found:
>  			      required_ext + inline_ext, false);
>  	if (ret < 0)
>  		return ERR_PTR(ret);
> +	if (ret != 0) {
> +		/* This buffer is not full yet, we may reuse it */
> +		reusing = malloc(sizeof(struct erofs_buffer_block_record));
> +		if (!reusing) {
> +			return ERR_PTR(-ENOMEM);
> +		}
> +		reusing->bb = bb;
> +		list_add_tail(&reusing->list,
> +				&non_full_buffer_blocks[type][EROFS_BLKSIZ - ret - inline_ext].list);
> +	}
>  	return bh;
>  }
>  
> @@ -247,8 +317,10 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
>  {
>  	erofs_blk_t blkaddr;
>  
> -	if (bb->blkaddr == NULL_ADDR)
> +	if (bb->blkaddr == NULL_ADDR) {
>  		bb->blkaddr = tail_blkaddr;
> +		last_mapped_block = bb;
> +	}
>  
>  	blkaddr = bb->blkaddr + BLK_ROUND_UP(bb->buffers.off);
>  	if (blkaddr > tail_blkaddr)
> @@ -259,15 +331,16 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
>  
>  erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb, bool end)
>  {
> -	struct erofs_buffer_block *t, *nt;
> -
> -	if (!bb || bb->blkaddr == NULL_ADDR) {
> -		list_for_each_entry_safe(t, nt, &blkh.list, list) {
> -			if (!end && (t == bb || nt == &blkh))
> -				break;
> -			(void)__erofs_mapbh(t);
> -			if (end && t == bb)
> -				break;
> +	DBG_BUGON(!end);
> +	struct erofs_buffer_block *t = last_mapped_block;
> +	while (1) {
> +		t = list_next_entry(t, list);
> +		if (t == &blkh) {
> +			break;
> +		}
> +		(void)__erofs_mapbh(t);
> +		if (t == bb) {
> +			break;
>  		}
>  	}
>  	return tail_blkaddr;
> @@ -334,6 +407,9 @@ void erofs_bdrop(struct erofs_buffer_head *bh, bool tryrevoke)
>  	if (!list_empty(&bb->buffers.list))
>  		return;
>  
> +	if (bb == last_mapped_block) {
> +		last_mapped_block = list_prev_entry(bb, list);
> +	}

same here.

Also, you could sent the patchset (I mean [PATCH 1/2],[PATCH 2/2]) entirely as a thread,
for example by using git send-email * --to="linux-erofs@lists.ozlabs" --cc="...."


Thanks,
Gao Xiang

>  	list_del(&bb->list);
>  	free(bb);
>  
> -- 
> 2.30.0
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 0/2] Optimize mkfs to O(N), N = #files
  2021-01-08 18:15 ` Gao Xiang
@ 2021-01-09  8:28   ` Hu Weiwen
  2021-01-09  8:28     ` [PATCH v2 1/2] erofs-utils: optimize " Hu Weiwen
  2021-01-09  8:28     ` [PATCH v2 2/2] erofs-utils: refactor: remove end argument from erofs_mapbh Hu Weiwen
  0 siblings, 2 replies; 7+ messages in thread
From: Hu Weiwen @ 2021-01-09  8:28 UTC (permalink / raw)
  To: Gao Xiang, Li Guifu, Miao Xie, Fang Wei; +Cc: Hu Weiwen, linux-erofs

Hi Xiang,

Thanks for your review.

Integrating the list_head to struct erofs_buffer_block seems better. And I removed the extra braces.

My previous patches were sent by "git send-email", but unfortunately, Outlook changed my Message-ID, so they did not appear in one thread. Sorry for that, I will try another email provider this time.

Hu Weiwen (2):
  erofs-utils: optimize mkfs to O(N), N = #files
  erofs-utils: refactor: remove end argument from erofs_mapbh

 include/erofs/cache.h |  3 +-
 lib/cache.c           | 90 ++++++++++++++++++++++++++++++++++++-------
 lib/compress.c        |  2 +-
 lib/inode.c           | 10 ++---
 lib/xattr.c           |  2 +-
 mkfs/main.c           |  2 +-
 6 files changed, 86 insertions(+), 23 deletions(-)

--
2.17.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v2 1/2] erofs-utils: optimize mkfs to O(N), N = #files
  2021-01-09  8:28   ` [PATCH v2 0/2] Optimize " Hu Weiwen
@ 2021-01-09  8:28     ` Hu Weiwen
  2021-01-09  8:28     ` [PATCH v2 2/2] erofs-utils: refactor: remove end argument from erofs_mapbh Hu Weiwen
  1 sibling, 0 replies; 7+ messages in thread
From: Hu Weiwen @ 2021-01-09  8:28 UTC (permalink / raw)
  To: Gao Xiang, Li Guifu, Miao Xie, Fang Wei; +Cc: Hu Weiwen, linux-erofs

When using EROFS to pack our dataset which consists of millions of
files, mkfs.erofs is very slow compared with mksquashfs.

The bottleneck is `erofs_balloc` and `erofs_mapbh` function, which
iterate over all previously allocated buffer blocks, making the
complexity of the algrithm O(N^2) where N is the number of files.

With this patch:

* global `last_mapped_block` is mantained to avoid full scan in
  `erofs_mapbh` function.

* global `non_full_buffer_blocks` mantains a list of buffer block for
  each type and each possible remaining bytes in the block. Then it is
  used to identify the most suitable blocks in future `erofs_balloc`,
  avoiding full scan.

Some new data structure is allocated in this patch, more RAM usage is
expected, but not much. When I test it with ImageNet dataset (1.33M
files), 7GiB RAM is consumed, and it takes about 4 hours. Most time is
spent on IO.

Signed-off-by: Hu Weiwen <sehuww@mail.scut.edu.cn>
---
 include/erofs/cache.h |  1 +
 lib/cache.c           | 89 ++++++++++++++++++++++++++++++++++++-------
 2 files changed, 77 insertions(+), 13 deletions(-)

diff --git a/include/erofs/cache.h b/include/erofs/cache.h
index 8c171f5..b5bf6c0 100644
--- a/include/erofs/cache.h
+++ b/include/erofs/cache.h
@@ -39,6 +39,7 @@ struct erofs_buffer_head {
 
 struct erofs_buffer_block {
 	struct list_head list;
+	struct list_head nonfull_list;
 
 	erofs_blk_t blkaddr;
 	int type;
diff --git a/lib/cache.c b/lib/cache.c
index 0d5c4a5..aa972d8 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -18,6 +18,15 @@ static struct erofs_buffer_block blkh = {
 };
 static erofs_blk_t tail_blkaddr;
 
+/**
+ * Some buffers are not full, we can reuse it to store more data
+ * 2 for DATA, META
+ * EROFS_BLKSIZ for each possible remaining bytes in the last block
+ **/
+static struct list_head nonfull_bb_buckets[2][EROFS_BLKSIZ];
+
+static struct erofs_buffer_block *last_mapped_block = &blkh;
+
 static bool erofs_bh_flush_drop_directly(struct erofs_buffer_head *bh)
 {
 	return erofs_bh_flush_generic_end(bh);
@@ -62,6 +71,10 @@ struct erofs_bhops erofs_buf_write_bhops = {
 /* return buffer_head of erofs super block (with size 0) */
 struct erofs_buffer_head *erofs_buffer_init(void)
 {
+	for (int i = 0; i < 2; i++)
+		for (int j = 0; j < EROFS_BLKSIZ; j++)
+			init_list_head(&nonfull_bb_buckets[i][j]);
+
 	struct erofs_buffer_head *bh = erofs_balloc(META, 0, 0, 0);
 
 	if (IS_ERR(bh))
@@ -131,7 +144,7 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
 {
 	struct erofs_buffer_block *cur, *bb;
 	struct erofs_buffer_head *bh;
-	unsigned int alignsize, used0, usedmax;
+	unsigned int alignsize, used0, usedmax, total_size;
 
 	int ret = get_alignsize(type, &type);
 
@@ -143,7 +156,36 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
 	usedmax = 0;
 	bb = NULL;
 
-	list_for_each_entry(cur, &blkh.list, list) {
+	erofs_dbg("balloc size=%lu alignsize=%u used0=%u", size, alignsize, used0);
+	if (used0 == 0 || alignsize == EROFS_BLKSIZ)
+		goto alloc;
+
+	total_size = size + required_ext + inline_ext;
+	if (total_size < EROFS_BLKSIZ) {
+		// Try find a most fit block.
+		DBG_BUGON(type < 0 || type > 1);
+		struct list_head *non_fulls = nonfull_bb_buckets[type];
+		for (struct list_head *r = non_fulls + round_up(total_size, alignsize);
+				r < non_fulls + EROFS_BLKSIZ; r++) {
+			if (!list_empty(r)) {
+				struct erofs_buffer_block *reuse_candidate =
+						list_first_entry(r, struct erofs_buffer_block, nonfull_list);
+				ret = __erofs_battach(reuse_candidate, NULL, size, alignsize,
+						required_ext + inline_ext, true);
+				if (ret >= 0) {
+					bb = reuse_candidate;
+					usedmax = (ret + required_ext) % EROFS_BLKSIZ + inline_ext;
+				}
+				break;
+			}
+		}
+	}
+
+	/* Try reuse last ones, which are not mapped and can be extended */
+	cur = last_mapped_block;
+	if (cur == &blkh)
+		cur = list_next_entry(cur, list);
+	for (; cur != &blkh; cur = list_next_entry(cur, list)) {
 		unsigned int used_before, used;
 
 		used_before = cur->buffers.off % EROFS_BLKSIZ;
@@ -181,16 +223,23 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
 	}
 
 	if (bb) {
+		erofs_dbg("reusing buffer. usedmax=%u", usedmax);
 		bh = malloc(sizeof(struct erofs_buffer_head));
 		if (!bh)
 			return ERR_PTR(-ENOMEM);
+		if (bb->nonfull_list.next != NULL)
+			list_del(&bb->nonfull_list);
 		goto found;
 	}
 
+alloc:
 	/* allocate a new buffer block */
-	if (used0 > EROFS_BLKSIZ)
+	if (used0 > EROFS_BLKSIZ) {
+		erofs_dbg("used0 > EROFS_BLKSIZ");
 		return ERR_PTR(-ENOSPC);
+	}
 
+	erofs_dbg("new buffer block");
 	bb = malloc(sizeof(struct erofs_buffer_block));
 	if (!bb)
 		return ERR_PTR(-ENOMEM);
@@ -211,6 +260,14 @@ found:
 			      required_ext + inline_ext, false);
 	if (ret < 0)
 		return ERR_PTR(ret);
+	if (ret == 0)
+		bb->nonfull_list.next = bb->nonfull_list.prev = NULL;
+	else {
+		/* This buffer is not full yet, we may reuse it */
+		int reusable_size = EROFS_BLKSIZ - ret - required_ext - inline_ext;
+		list_add_tail(&bb->nonfull_list, &nonfull_bb_buckets[type][reusable_size]);
+	}
+
 	return bh;
 }
 
@@ -247,8 +304,10 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
 {
 	erofs_blk_t blkaddr;
 
-	if (bb->blkaddr == NULL_ADDR)
+	if (bb->blkaddr == NULL_ADDR) {
 		bb->blkaddr = tail_blkaddr;
+		last_mapped_block = bb;
+	}
 
 	blkaddr = bb->blkaddr + BLK_ROUND_UP(bb->buffers.off);
 	if (blkaddr > tail_blkaddr)
@@ -259,15 +318,16 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
 
 erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb, bool end)
 {
-	struct erofs_buffer_block *t, *nt;
-
-	if (!bb || bb->blkaddr == NULL_ADDR) {
-		list_for_each_entry_safe(t, nt, &blkh.list, list) {
-			if (!end && (t == bb || nt == &blkh))
-				break;
-			(void)__erofs_mapbh(t);
-			if (end && t == bb)
-				break;
+	DBG_BUGON(!end);
+	struct erofs_buffer_block *t = last_mapped_block;
+	while (1) {
+		t = list_next_entry(t, list);
+		if (t == &blkh) {
+			break;
+		}
+		(void)__erofs_mapbh(t);
+		if (t == bb) {
+			break;
 		}
 	}
 	return tail_blkaddr;
@@ -334,6 +394,9 @@ void erofs_bdrop(struct erofs_buffer_head *bh, bool tryrevoke)
 	if (!list_empty(&bb->buffers.list))
 		return;
 
+	if (bb == last_mapped_block)
+		last_mapped_block = list_prev_entry(bb, list);
+
 	list_del(&bb->list);
 	free(bb);
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v2 2/2] erofs-utils: refactor: remove end argument from erofs_mapbh
  2021-01-09  8:28   ` [PATCH v2 0/2] Optimize " Hu Weiwen
  2021-01-09  8:28     ` [PATCH v2 1/2] erofs-utils: optimize " Hu Weiwen
@ 2021-01-09  8:28     ` Hu Weiwen
  1 sibling, 0 replies; 7+ messages in thread
From: Hu Weiwen @ 2021-01-09  8:28 UTC (permalink / raw)
  To: Gao Xiang, Li Guifu, Miao Xie, Fang Wei; +Cc: Hu Weiwen, linux-erofs

Signed-off-by: Hu Weiwen <sehuww@mail.scut.edu.cn>
---
 include/erofs/cache.h |  2 +-
 lib/cache.c           |  3 +--
 lib/compress.c        |  2 +-
 lib/inode.c           | 10 +++++-----
 lib/xattr.c           |  2 +-
 mkfs/main.c           |  2 +-
 6 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/include/erofs/cache.h b/include/erofs/cache.h
index b5bf6c0..26d341e 100644
--- a/include/erofs/cache.h
+++ b/include/erofs/cache.h
@@ -96,7 +96,7 @@ struct erofs_buffer_head *erofs_balloc(int type, erofs_off_t size,
 struct erofs_buffer_head *erofs_battach(struct erofs_buffer_head *bh,
 					int type, unsigned int size);
 
-erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb, bool end);
+erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb);
 bool erofs_bflush(struct erofs_buffer_block *bb);
 
 void erofs_bdrop(struct erofs_buffer_head *bh, bool tryrevoke);
diff --git a/lib/cache.c b/lib/cache.c
index aa972d8..bddb237 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -316,9 +316,8 @@ static erofs_blk_t __erofs_mapbh(struct erofs_buffer_block *bb)
 	return blkaddr;
 }
 
-erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb, bool end)
+erofs_blk_t erofs_mapbh(struct erofs_buffer_block *bb)
 {
-	DBG_BUGON(!end);
 	struct erofs_buffer_block *t = last_mapped_block;
 	while (1) {
 		t = list_next_entry(t, list);
diff --git a/lib/compress.c b/lib/compress.c
index 86db940..2b1f93c 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -416,7 +416,7 @@ int erofs_write_compressed_file(struct erofs_inode *inode)
 
 	memset(compressmeta, 0, Z_EROFS_LEGACY_MAP_HEADER_SIZE);
 
-	blkaddr = erofs_mapbh(bh->block, true);	/* start_blkaddr */
+	blkaddr = erofs_mapbh(bh->block);	/* start_blkaddr */
 	ctx.blkaddr = blkaddr;
 	ctx.metacur = compressmeta + Z_EROFS_LEGACY_MAP_HEADER_SIZE;
 	ctx.head = ctx.tail = 0;
diff --git a/lib/inode.c b/lib/inode.c
index d0b4d51..4ed6aed 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -148,7 +148,7 @@ static int __allocate_inode_bh_data(struct erofs_inode *inode,
 	inode->bh_data = bh;
 
 	/* get blkaddr of the bh */
-	ret = erofs_mapbh(bh->block, true);
+	ret = erofs_mapbh(bh->block);
 	DBG_BUGON(ret < 0);
 
 	/* write blocks except for the tail-end block */
@@ -522,7 +522,7 @@ int erofs_prepare_tail_block(struct erofs_inode *inode)
 		bh->op = &erofs_skip_write_bhops;
 
 		/* get blkaddr of bh */
-		ret = erofs_mapbh(bh->block, true);
+		ret = erofs_mapbh(bh->block);
 		DBG_BUGON(ret < 0);
 		inode->u.i_blkaddr = bh->block->blkaddr;
 
@@ -632,7 +632,7 @@ int erofs_write_tail_end(struct erofs_inode *inode)
 		int ret;
 		erofs_off_t pos;
 
-		erofs_mapbh(bh->block, true);
+		erofs_mapbh(bh->block);
 		pos = erofs_btell(bh, true) - EROFS_BLKSIZ;
 		ret = dev_write(inode->idata, pos, inode->idata_size);
 		if (ret)
@@ -879,7 +879,7 @@ void erofs_fixup_meta_blkaddr(struct erofs_inode *rootdir)
 	struct erofs_buffer_head *const bh = rootdir->bh;
 	erofs_off_t off, meta_offset;
 
-	erofs_mapbh(bh->block, true);
+	erofs_mapbh(bh->block);
 	off = erofs_btell(bh, false);
 
 	if (off > rootnid_maxoffset)
@@ -898,7 +898,7 @@ erofs_nid_t erofs_lookupnid(struct erofs_inode *inode)
 	if (!bh)
 		return inode->nid;
 
-	erofs_mapbh(bh->block, true);
+	erofs_mapbh(bh->block);
 	off = erofs_btell(bh, false);
 
 	meta_offset = blknr_to_addr(sbi.meta_blkaddr);
diff --git a/lib/xattr.c b/lib/xattr.c
index 49ebb9c..8b7bcb1 100644
--- a/lib/xattr.c
+++ b/lib/xattr.c
@@ -575,7 +575,7 @@ int erofs_build_shared_xattrs_from_path(const char *path)
 	}
 	bh->op = &erofs_skip_write_bhops;
 
-	erofs_mapbh(bh->block, true);
+	erofs_mapbh(bh->block);
 	off = erofs_btell(bh, false);
 
 	sbi.xattr_blkaddr = off / EROFS_BLKSIZ;
diff --git a/mkfs/main.c b/mkfs/main.c
index abd48be..d9c4c7f 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -304,7 +304,7 @@ int erofs_mkfs_update_super_block(struct erofs_buffer_head *bh,
 		round_up(EROFS_SUPER_END, EROFS_BLKSIZ);
 	char *buf;
 
-	*blocks         = erofs_mapbh(NULL, true);
+	*blocks         = erofs_mapbh(NULL);
 	sb.blocks       = cpu_to_le32(*blocks);
 	sb.root_nid     = cpu_to_le16(root_nid);
 	memcpy(sb.uuid, sbi.uuid, sizeof(sb.uuid));
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-01-09  8:48 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-01-01  9:11 [PATCH 1/2] erofs-utils: optimize mkfs to O(N), N = #files 胡玮文
2021-01-01  9:50 ` 胡 玮文
2021-01-02  5:09   ` Gao Xiang
2021-01-08 18:15 ` Gao Xiang
2021-01-09  8:28   ` [PATCH v2 0/2] Optimize " Hu Weiwen
2021-01-09  8:28     ` [PATCH v2 1/2] erofs-utils: optimize " Hu Weiwen
2021-01-09  8:28     ` [PATCH v2 2/2] erofs-utils: refactor: remove end argument from erofs_mapbh Hu Weiwen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.