Commit d8554009 d85540094f0df7258af1cae7c61cc6d45c2a4f53 by Sergey Poznyakoff

mh,maildir: speed up scanning.

* include/mailutils/sys/amd.h (_amd_message_append, amd_sort): New protos.
* libmailutils/base/amd.c (amd_array_expand): Call memmove only if there is
actually something to move.
(_amd_message_append, amd_sort): New functions.
* libproto/maildir/mbox.c (maildir_scan_dir): Append new messages to the
end of the array, and sort it afterwards. This avoids unnecessary memory
moves, which improves performance considerably, especially on large
mailboxes.
* libproto/mh/mbox.c (mh_scan0): Likewise.
1 parent 42838314
......@@ -110,6 +110,8 @@ int _amd_message_lookup_or_insert (struct _amd_data *amd,
struct _amd_message *key,
size_t *pindex);
int _amd_message_insert (struct _amd_data *mhd, struct _amd_message *msg);
int _amd_message_append (struct _amd_data *amd, struct _amd_message *msg);
void amd_sort (struct _amd_data *amd);
int amd_message_stream_open (struct _amd_message *mhm);
void amd_message_stream_close (struct _amd_message *mhm);
void amd_cleanup (void *arg);
......
......@@ -241,8 +241,9 @@ amd_array_expand (struct _amd_data *amd, size_t index)
}
amd->msg_array = p;
}
memmove (&amd->msg_array[index+1], &amd->msg_array[index],
(amd->msg_count-index) * amd->msg_size);
if (amd->msg_count > index)
memmove (&amd->msg_array[index+1], &amd->msg_array[index],
(amd->msg_count-index) * amd->msg_size);
amd->msg_count++;
return 0;
}
......@@ -1381,6 +1382,35 @@ _amd_message_insert (struct _amd_data *amd, struct _amd_message *msg)
return 0;
}
/* Append message to the end of the array, expanding it if necessary */
int
_amd_message_append (struct _amd_data *amd, struct _amd_message *msg)
{
size_t index = amd->msg_count;
int rc = amd_array_expand (amd, index);
if (rc)
return rc;
amd->msg_array[index] = msg;
msg->amd = amd;
return 0;
}
static int
msg_array_comp (const void *a, const void *b)
{
struct _amd_message **ma = (struct _amd_message **) a;
struct _amd_message **mb = (struct _amd_message **) b;
struct _amd_data *amd = (*ma)->amd;
return amd->msg_cmp (*ma, *mb);
}
void
amd_sort (struct _amd_data *amd)
{
qsort (amd->msg_array, amd->msg_count, sizeof (amd->msg_array[0]),
msg_array_comp);
}
static void
_amd_message_delete (struct _amd_data *amd, struct _amd_message *msg)
{
......
......@@ -584,7 +584,8 @@ maildir_scan_dir (struct _amd_data *amd, DIR *dir, char *dirname)
char *p;
size_t index;
int rc = 0;
int need_sort = 0;
while ((entry = readdir (dir)))
{
switch (entry->d_name[0])
......@@ -593,31 +594,23 @@ maildir_scan_dir (struct _amd_data *amd, DIR *dir, char *dirname)
break;
default:
key.file_name = entry->d_name;
rc = _amd_message_lookup_or_insert (amd,
(struct _amd_message *)&key,
&index);
if (rc == MU_ERR_NOENT)
{
/* Message not found. Index pointd to the array cell where it
would be placed */
msg = calloc (1, sizeof (*msg));
if (!msg)
{
rc = ENOMEM;
break;
}
amd->msg_array[index] = (struct _amd_message *)msg;
msg->amd_message.amd = amd;
rc = 0;
msg = calloc (1, sizeof (*msg));
if (!msg)
{
rc = ENOMEM;
break;
}
else if (rc == 0)
key.file_name = entry->d_name;
if (!amd_msg_lookup (amd, (struct _amd_message *) &key, &index))
continue;
rc = _amd_message_append (amd, (struct _amd_message *) msg);
if (rc)
{
msg = (struct _maildir_message *)amd->msg_array[index];
free (msg->file_name);
free (msg);
break;
}
else
break;
msg->dir = dirname;
msg->file_name = strdup (entry->d_name);
......@@ -628,9 +621,12 @@ maildir_scan_dir (struct _amd_data *amd, DIR *dir, char *dirname)
else
msg->amd_message.attr_flags = 0;
msg->amd_message.orig_flags = msg->amd_message.attr_flags;
need_sort = 1;
}
}
if (rc == 0 && need_sort)
amd_sort (amd);
return rc;
}
......
......@@ -181,6 +181,7 @@ mh_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED, size_t *pcount,
struct dirent *entry;
int status = 0;
struct stat st;
int need_sort = 0;
if (amd == NULL)
return EINVAL;
......@@ -235,12 +236,18 @@ mh_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED, size_t *pcount,
if (!msg)
{
msg = calloc (1, sizeof(*msg));
status = _amd_message_append (amd, (struct _amd_message *) msg);
if (status)
{
free (msg);
break;
}
msg->seq_number = num;
msg->amd_message.attr_flags = attr_flags;
msg->amd_message.orig_flags = msg->amd_message.attr_flags;
_amd_message_insert (amd, (struct _amd_message*) msg);
need_sort = 1;
}
else
{
......@@ -251,38 +258,43 @@ mh_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED, size_t *pcount,
closedir (dir);
if (do_notify)
{
size_t i;
if (need_sort)
amd_sort (amd);
for (i = 0; i < amd->msg_count; i++)
if (status == 0)
{
if (do_notify)
{
DISPATCH_ADD_MSG (mailbox, amd, i);
size_t i;
for (i = 0; i < amd->msg_count; i++)
{
DISPATCH_ADD_MSG (mailbox, amd, i);
}
}
}
if (stat (amd->name, &st) == 0)
amd->mtime = st.st_mtime;
if (stat (amd->name, &st) == 0)
amd->mtime = st.st_mtime;
if (pcount)
*pcount = amd->msg_count;
if (pcount)
*pcount = amd->msg_count;
/* Reset the uidvalidity. */
if (amd->msg_count > 0)
{
if (amd->uidvalidity == 0)
/* Reset the uidvalidity. */
if (amd->msg_count > 0)
{
amd->uidvalidity = (unsigned long)time (NULL);
/* Tell that we have been modified for expunging. */
if (amd->msg_count)
if (amd->uidvalidity == 0)
{
amd_message_stream_open (amd->msg_array[0]);
amd_message_stream_close (amd->msg_array[0]);
amd->msg_array[0]->attr_flags |= MU_ATTRIBUTE_MODIFIED;
amd->uidvalidity = (unsigned long)time (NULL);
/* Tell that we have been modified for expunging. */
if (amd->msg_count)
{
amd_message_stream_open (amd->msg_array[0]);
amd_message_stream_close (amd->msg_array[0]);
amd->msg_array[0]->attr_flags |= MU_ATTRIBUTE_MODIFIED;
}
}
}
}
/* Clean up the things */
amd_cleanup (mailbox);
......