~rjarry/aerc#16: 
Maildir performance regression

Recent aerc releases perform much more poorly on large maildirs than the previous versions. I have a maildir with 40,000+ emails, and though I could previously open it in less than a second, it now takes upwards of 60 seconds.

Status
RESOLVED FIXED
Submitter
~sircmpwn
Assigned to
Submitted
3 months ago
Updated
a month ago
Labels
maildir

~rjarry 3 months ago

Do you recall a version with which the performance was better? That would help pinpointing the issue.

~sircmpwn 3 months ago

I suspect that it was better in 0.6.0, but I'm not certain if I had moved onto your fork before or after 0.7.0. I've been running a weird mix of Alpine packages and a local build. It may be possible to reproduce this locally by writing a script to generate a large maildir and doing a git bisect, I can do this at some point if you don't have time but I'm not sure when I'll be able to get back to you.

~rjarry 3 months ago

Thanks, I can take care of it. I'll have a quick look tonight.

~rjarry 3 months ago

Hi Koni,

Koni Marti, Jan 26, 2022 at 12:06:

Hi Robin,

I'm writing with regard to the maildir performance regression issue that you are assigned to.

I believe that my recent commit (b30909 "dirlist: skip unnecessary change-folder action") added 1 second to the startup time. Hence, I'm partly to blame and would like to offer my support, if needed at all.

I have a attached a quick summary of my findings so far hoping that it can be useful in finding the root causes and that it aligns with your analysis.

Thanks a lot. I had not started working on it, I got caught up in something else yesterday.

I am CC'ing the ticket with your findings, I hope that is OK.

Approach

A sample maildir with 400'000 messages (total 3.1G) was used for the performance tests.

The performance was estimated by running time ./aerc and quitting when the message view was completely loaded. The performance metric is the total execution time, which is averaged over two runs. Individual timings are obviously machine dependent, but the overall trend is relevant.

Results

Tag Time Commit causing bulk of time increase
master 11.7s cb3090956cfd
0.7.1 10.7s
0.7.0 10.7s
0.6.0 10.7s
0.5.0 11.0s
0.4.0 11.1s 01c96e78dfe8
0.3.0 5.3s 43435ba06cd0
0.2.0 1.5s

Great work!

Discussion

A performance regression over the recent releases can be demonstrated. The majority of the observed time increase is causes by release 0.4.0

The large increase in the startup time is due to the getDirectoryInfo() function in the maildir worker. This function loops over all messages and determines the number of seen and recent mails.

I guess this is required to update the folder view. However it could be made asynchronously after the folder has been opened to have a more responsive UI.

Another time increase of ~1s was introduced by my recent commit to avoid loading folders that are skipped over anyway. This can be reduced to 500ms, completely removed or have the user choose a suitable time himself.

I wonder if that could even be reduced to 300ms. Why not have an option for this but I don't think it is that important if the default value is suitable for most people.

Proof of principle

The following patch is to verify the claim that the largest time increase is caused by the getDirectoryInfo function and the skip-folder wait. This patch, applied to the master, reduces the startup time from 11.7s to 3.3s by only calculating the total mails in a folder and not the seen/recent messages:

diff --git a/widgets/dirlist.go b/widgets/dirlist.go
index 53afc1d..b7e8538 100644
--- a/widgets/dirlist.go
+++ b/widgets/dirlist.go
@@ -96,7 +96,7 @@ func (dirlist *DirectoryList) Select(name string) {

        go func() {
                select {
-               case <-time.After(1 * time.Second):
+               case <-time.After(500 * time.Millisecond):
                        dirlist.worker.PostAction(&types.OpenDirectory{Directory: name},
                                func(msg types.WorkerMessage) {
                                        switch msg.(type) {
diff --git a/worker/maildir/worker.go b/worker/maildir/worker.go
index 2f75f12..df8af4d 100644
--- a/worker/maildir/worker.go
+++ b/worker/maildir/worker.go
@@ -143,6 +143,9 @@ func (w *Worker) getDirectoryInfo(name string) *models.DirectoryInfo {
                return dirInfo
        }

+       dirInfo.Exists = len(uids)
+       return dirInfo
+
        for _, uid := range uids {
                message, err := w.c.Message(dir, uid)
                if err != nil {

Thoughts?

~rjarry 3 months ago

Robin Jarry referenced this ticket in commit a5c046e.

~rjarry REPORTED FIXED 3 months ago

Robin Jarry referenced this ticket in commit 622802d.

~sircmpwn 3 months ago

Thanks for taking care of this!

~rjarry 2 months ago

Robin Jarry referenced this ticket in commit a5c046e.

~rjarry 2 months ago

Robin Jarry referenced this ticket in commit 622802d.

~inwit 2 months ago*

Maybe this is unrelated, but I'm also experiencing long delays in Maildir (over 20s for a folder with ~20.000 messages). In the logs, I'm getting a lot of errors "could not get flags: maildir: invalid mailfile format". My file names are set by mbsync (and do have flags properly declared, afaik). The same problem affects the notmuch interface too.

EDIT: the errors were due to a faulty patch that has now been solved. However, the long times are still there and I still get other error messages as in https://github.com/emersion/go-maildir/issues/12

~poldi1405 a month ago

Robin Jarry referenced this ticket in commit a5c046e.

Register here or Log in to comment, or comment via email.