[PATCH] erofs-utils: lib: introduce the merge sort algorithm for dentries
Hongzhen Luo
hongzhen at linux.alibaba.com
Wed Sep 4 21:34:48 AEST 2024
Currently, malloc(), qsort() and free() are used to sort the dentries
in the directory. This patch reuses the existing `dir->i_subdirs` list and
the merge sort to achieve the same objective. This may avoid the potential
performance degradation when using malloc() and free().
Signed-off-by: Hongzhen Luo <hongzhen at linux.alibaba.com>
---
lib/inode.c | 111 +++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 96 insertions(+), 15 deletions(-)
diff --git a/lib/inode.c b/lib/inode.c
index 128c051..2283fc0 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -234,27 +234,108 @@ int erofs_init_empty_dir(struct erofs_inode *dir)
return 0;
}
+/* given list p and q that are two circles, return a merged circle */
+static struct list_head *erofs_subdirs_merge(struct list_head *p,
+ struct list_head *q)
+{
+ struct list_head *cur, *ret = NULL;
+ struct erofs_dentry *d1, *d2;
+
+ if (p)
+ p->prev->next = NULL;
+ if (q)
+ q->prev->next = NULL;
+
+ while (p || q) {
+ d1 = d2 = NULL;
+ if (p)
+ d1 = container_of(p, struct erofs_dentry, d_child);
+ if (q)
+ d2 = container_of(q, struct erofs_dentry, d_child);
+ if (d1 && d2) {
+ if (comp_subdir(&d1, &d2) < 0) {
+ cur = p;
+ p = p->next;
+ } else {
+ cur = q;
+ q = q->next;
+ }
+ } else if (d1) {
+ cur = p;
+ p = p->next;
+ } else {
+ cur = q;
+ q = q->next;
+ }
+ if (!ret) {
+ ret = cur;
+ init_list_head(ret);
+ } else
+ list_add_tail(cur, ret);
+ }
+ return ret;
+}
+
+static struct list_head *erofs_subdirs_sort(struct list_head *lst,
+ unsigned int nr_subdirs)
+{
+ struct list_head *p, *q;
+ int i;
+
+ if (!lst || !nr_subdirs) {
+ erofs_err("invalid lst or nr_subdirs");
+ return NULL;
+ }
+ if (nr_subdirs == 1) {
+ init_list_head(lst);
+ return lst;
+ }
+ p = lst;
+ for (i = 0; i < nr_subdirs / 2 - 1; i ++) {
+ p = p->next;
+ }
+ q = p->next;
+
+ /* split into two circles */
+ q->prev = lst->prev;
+ lst->prev->next = q;
+ lst->prev = p;
+ p->next = lst;
+
+ p = erofs_subdirs_sort(p, nr_subdirs / 2);
+ q = erofs_subdirs_sort(q, nr_subdirs - nr_subdirs / 2);
+ return erofs_subdirs_merge(p, q);
+}
+
+static void erofs_sort_subdirs_list(struct list_head *head,
+ unsigned int nr_subdirs)
+{
+ struct list_head *lst;
+
+ if (!head || nr_subdirs < 2) {
+ erofs_err("nr_subdirs should >= 2");
+ return;
+ }
+ lst = erofs_subdirs_sort(head->next, nr_subdirs);
+ if (!lst) {
+ erofs_err("fail to sort subdirs");
+ return;
+ }
+ /* attach the head to the sorted list */
+ lst->prev->next = head;
+ head->prev = lst->prev;
+ lst->prev = head;
+ head->next = lst;
+}
+
static int erofs_prepare_dir_file(struct erofs_inode *dir,
unsigned int nr_subdirs)
{
struct erofs_sb_info *sbi = dir->sbi;
- struct erofs_dentry *d, *n, **sorted_d;
- unsigned int i;
+ struct erofs_dentry *d;
unsigned int d_size = 0;
- sorted_d = malloc(nr_subdirs * sizeof(d));
- if (!sorted_d)
- return -ENOMEM;
- i = 0;
- list_for_each_entry_safe(d, n, &dir->i_subdirs, d_child) {
- list_del(&d->d_child);
- sorted_d[i++] = d;
- }
- DBG_BUGON(i != nr_subdirs);
- qsort(sorted_d, nr_subdirs, sizeof(d), comp_subdir);
- for (i = 0; i < nr_subdirs; i++)
- list_add_tail(&sorted_d[i]->d_child, &dir->i_subdirs);
- free(sorted_d);
+ erofs_sort_subdirs_list(&dir->i_subdirs, nr_subdirs);
/* let's calculate dir size */
list_for_each_entry(d, &dir->i_subdirs, d_child) {
--
2.43.5
More information about the Linux-erofs
mailing list