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, ...@@ -110,6 +110,8 @@ int _amd_message_lookup_or_insert (struct _amd_data *amd,
110 struct _amd_message *key, 110 struct _amd_message *key,
111 size_t *pindex); 111 size_t *pindex);
112 int _amd_message_insert (struct _amd_data *mhd, struct _amd_message *msg); 112 int _amd_message_insert (struct _amd_data *mhd, struct _amd_message *msg);
113 int _amd_message_append (struct _amd_data *amd, struct _amd_message *msg);
114 void amd_sort (struct _amd_data *amd);
113 int amd_message_stream_open (struct _amd_message *mhm); 115 int amd_message_stream_open (struct _amd_message *mhm);
114 void amd_message_stream_close (struct _amd_message *mhm); 116 void amd_message_stream_close (struct _amd_message *mhm);
115 void amd_cleanup (void *arg); 117 void amd_cleanup (void *arg);
......
...@@ -241,8 +241,9 @@ amd_array_expand (struct _amd_data *amd, size_t index) ...@@ -241,8 +241,9 @@ amd_array_expand (struct _amd_data *amd, size_t index)
241 } 241 }
242 amd->msg_array = p; 242 amd->msg_array = p;
243 } 243 }
244 memmove (&amd->msg_array[index+1], &amd->msg_array[index], 244 if (amd->msg_count > index)
245 (amd->msg_count-index) * amd->msg_size); 245 memmove (&amd->msg_array[index+1], &amd->msg_array[index],
246 (amd->msg_count-index) * amd->msg_size);
246 amd->msg_count++; 247 amd->msg_count++;
247 return 0; 248 return 0;
248 } 249 }
...@@ -1381,6 +1382,35 @@ _amd_message_insert (struct _amd_data *amd, struct _amd_message *msg) ...@@ -1381,6 +1382,35 @@ _amd_message_insert (struct _amd_data *amd, struct _amd_message *msg)
1381 return 0; 1382 return 0;
1382 } 1383 }
1383 1384
1385 /* Append message to the end of the array, expanding it if necessary */
1386 int
1387 _amd_message_append (struct _amd_data *amd, struct _amd_message *msg)
1388 {
1389 size_t index = amd->msg_count;
1390 int rc = amd_array_expand (amd, index);
1391 if (rc)
1392 return rc;
1393 amd->msg_array[index] = msg;
1394 msg->amd = amd;
1395 return 0;
1396 }
1397
1398 static int
1399 msg_array_comp (const void *a, const void *b)
1400 {
1401 struct _amd_message **ma = (struct _amd_message **) a;
1402 struct _amd_message **mb = (struct _amd_message **) b;
1403 struct _amd_data *amd = (*ma)->amd;
1404 return amd->msg_cmp (*ma, *mb);
1405 }
1406
1407 void
1408 amd_sort (struct _amd_data *amd)
1409 {
1410 qsort (amd->msg_array, amd->msg_count, sizeof (amd->msg_array[0]),
1411 msg_array_comp);
1412 }
1413
1384 static void 1414 static void
1385 _amd_message_delete (struct _amd_data *amd, struct _amd_message *msg) 1415 _amd_message_delete (struct _amd_data *amd, struct _amd_message *msg)
1386 { 1416 {
......
...@@ -584,7 +584,8 @@ maildir_scan_dir (struct _amd_data *amd, DIR *dir, char *dirname) ...@@ -584,7 +584,8 @@ maildir_scan_dir (struct _amd_data *amd, DIR *dir, char *dirname)
584 char *p; 584 char *p;
585 size_t index; 585 size_t index;
586 int rc = 0; 586 int rc = 0;
587 587 int need_sort = 0;
588
588 while ((entry = readdir (dir))) 589 while ((entry = readdir (dir)))
589 { 590 {
590 switch (entry->d_name[0]) 591 switch (entry->d_name[0])
...@@ -593,31 +594,23 @@ maildir_scan_dir (struct _amd_data *amd, DIR *dir, char *dirname) ...@@ -593,31 +594,23 @@ maildir_scan_dir (struct _amd_data *amd, DIR *dir, char *dirname)
593 break; 594 break;
594 595
595 default: 596 default:
596 key.file_name = entry->d_name;
597 rc = _amd_message_lookup_or_insert (amd,
598 (struct _amd_message *)&key,
599 &index);
600 if (rc == MU_ERR_NOENT)
601 {
602 /* Message not found. Index pointd to the array cell where it 597 /* Message not found. Index pointd to the array cell where it
603 would be placed */ 598 would be placed */
604 msg = calloc (1, sizeof (*msg)); 599 msg = calloc (1, sizeof (*msg));
605 if (!msg) 600 if (!msg)
606 { 601 {
607 rc = ENOMEM; 602 rc = ENOMEM;
608 break; 603 break;
609 }
610 amd->msg_array[index] = (struct _amd_message *)msg;
611 msg->amd_message.amd = amd;
612 rc = 0;
613 } 604 }
614 else if (rc == 0) 605 key.file_name = entry->d_name;
606 if (!amd_msg_lookup (amd, (struct _amd_message *) &key, &index))
607 continue;
608 rc = _amd_message_append (amd, (struct _amd_message *) msg);
609 if (rc)
615 { 610 {
616 msg = (struct _maildir_message *)amd->msg_array[index]; 611 free (msg);
617 free (msg->file_name); 612 break;
618 } 613 }
619 else
620 break;
621 614
622 msg->dir = dirname; 615 msg->dir = dirname;
623 msg->file_name = strdup (entry->d_name); 616 msg->file_name = strdup (entry->d_name);
...@@ -628,9 +621,12 @@ maildir_scan_dir (struct _amd_data *amd, DIR *dir, char *dirname) ...@@ -628,9 +621,12 @@ maildir_scan_dir (struct _amd_data *amd, DIR *dir, char *dirname)
628 else 621 else
629 msg->amd_message.attr_flags = 0; 622 msg->amd_message.attr_flags = 0;
630 msg->amd_message.orig_flags = msg->amd_message.attr_flags; 623 msg->amd_message.orig_flags = msg->amd_message.attr_flags;
624 need_sort = 1;
631 } 625 }
632 } 626 }
633 627
628 if (rc == 0 && need_sort)
629 amd_sort (amd);
634 return rc; 630 return rc;
635 } 631 }
636 632
......
...@@ -181,6 +181,7 @@ mh_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED, size_t *pcount, ...@@ -181,6 +181,7 @@ mh_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED, size_t *pcount,
181 struct dirent *entry; 181 struct dirent *entry;
182 int status = 0; 182 int status = 0;
183 struct stat st; 183 struct stat st;
184 int need_sort = 0;
184 185
185 if (amd == NULL) 186 if (amd == NULL)
186 return EINVAL; 187 return EINVAL;
...@@ -235,12 +236,18 @@ mh_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED, size_t *pcount, ...@@ -235,12 +236,18 @@ mh_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED, size_t *pcount,
235 if (!msg) 236 if (!msg)
236 { 237 {
237 msg = calloc (1, sizeof(*msg)); 238 msg = calloc (1, sizeof(*msg));
239 status = _amd_message_append (amd, (struct _amd_message *) msg);
240 if (status)
241 {
242 free (msg);
243 break;
244 }
238 245
239 msg->seq_number = num; 246 msg->seq_number = num;
240 msg->amd_message.attr_flags = attr_flags; 247 msg->amd_message.attr_flags = attr_flags;
241 msg->amd_message.orig_flags = msg->amd_message.attr_flags; 248 msg->amd_message.orig_flags = msg->amd_message.attr_flags;
242 249
243 _amd_message_insert (amd, (struct _amd_message*) msg); 250 need_sort = 1;
244 } 251 }
245 else 252 else
246 { 253 {
...@@ -251,38 +258,43 @@ mh_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED, size_t *pcount, ...@@ -251,38 +258,43 @@ mh_scan0 (mu_mailbox_t mailbox, size_t msgno MU_ARG_UNUSED, size_t *pcount,
251 258
252 closedir (dir); 259 closedir (dir);
253 260
254 if (do_notify) 261 if (need_sort)
255 { 262 amd_sort (amd);
256 size_t i;
257 263
258 for (i = 0; i < amd->msg_count; i++) 264 if (status == 0)
265 {
266 if (do_notify)
259 { 267 {
260 DISPATCH_ADD_MSG (mailbox, amd, i); 268 size_t i;
269
270 for (i = 0; i < amd->msg_count; i++)
271 {
272 DISPATCH_ADD_MSG (mailbox, amd, i);
273 }
261 } 274 }
262 }
263 275
264 if (stat (amd->name, &st) == 0) 276 if (stat (amd->name, &st) == 0)
265 amd->mtime = st.st_mtime; 277 amd->mtime = st.st_mtime;
266 278
267 if (pcount) 279 if (pcount)
268 *pcount = amd->msg_count; 280 *pcount = amd->msg_count;
269 281
270 /* Reset the uidvalidity. */ 282 /* Reset the uidvalidity. */
271 if (amd->msg_count > 0) 283 if (amd->msg_count > 0)
272 {
273 if (amd->uidvalidity == 0)
274 { 284 {
275 amd->uidvalidity = (unsigned long)time (NULL); 285 if (amd->uidvalidity == 0)
276 /* Tell that we have been modified for expunging. */
277 if (amd->msg_count)
278 { 286 {
279 amd_message_stream_open (amd->msg_array[0]); 287 amd->uidvalidity = (unsigned long)time (NULL);
280 amd_message_stream_close (amd->msg_array[0]); 288 /* Tell that we have been modified for expunging. */
281 amd->msg_array[0]->attr_flags |= MU_ATTRIBUTE_MODIFIED; 289 if (amd->msg_count)
290 {
291 amd_message_stream_open (amd->msg_array[0]);
292 amd_message_stream_close (amd->msg_array[0]);
293 amd->msg_array[0]->attr_flags |= MU_ATTRIBUTE_MODIFIED;
294 }
282 } 295 }
283 } 296 }
284 } 297 }
285
286 /* Clean up the things */ 298 /* Clean up the things */
287 299
288 amd_cleanup (mailbox); 300 amd_cleanup (mailbox);
......