~kevin8t8/mutt

0f8a079d1cc66878ddfa68476f8ea7abdb6fd7e8 — Kevin McCarthy a month ago 376716e
Speed up thread sort when many long threads exist.

Merge request !75 reported a massive slowdown opening a mailbox with
many long threads: on the order of an hour.

This is because mutt_set_virtual() was iterating through the whole
thread for each message.

After taking a closer look at current Mutt behavior, it seems the
num_hidden count is only needed in the root thread.  Quoting my
comment in the merge request:

  The index-format expando %M only applies to root level headers, so
  there are no issues there.

  The only concern is the pattern ~v. Limiting always resets collapsed
  and num_hidden, so there also can't be any issues there. Tagging and
  other pattern operators only work on visible messages. So tagging
  while threads are sorted will only tag the root messages of
  collapsed threads.

  However, if you sort by thread, collapse a thread, and then sort by
  date, the collapsed and num_hidden fields are not reset for each
  header. In theory this would allow us to tag ~v with date
  ordering. However, in master the num_hidden is only set (by
  mutt_set_virtual()) for visible messages with a virtual number.

  So even in master, switching to date-order and tagging ~v will only
  tag the root messages of each collapsed thread.

This commit removes the num_hidden setting in mutt_set_virtual(),
instead directly assigning that value in the roothdr during a collapse
operation.

A subsequent commit will fix the behavior of ~v, when switching
sorting from threaded to something else, by putting num_hidden in
every header in the thread.  This is technically a change in behavior,
so I will make that commit in the master branch.

Thanks to Score_Under for the pull request and for testing my
alternate solutions.
3 files changed, 14 insertions(+), 11 deletions(-)

M mutt.h
M protos.h
M thread.c
M mutt.h => mutt.h +5 -4
@@ 203,9 203,8 @@ typedef enum

#define MUTT_THREAD_COLLAPSE    (1<<0)
#define MUTT_THREAD_UNCOLLAPSE  (1<<1)
#define MUTT_THREAD_GET_HIDDEN  (1<<2)
#define MUTT_THREAD_UNREAD      (1<<3)
#define MUTT_THREAD_NEXT_UNREAD (1<<4)
#define MUTT_THREAD_UNREAD      (1<<2)
#define MUTT_THREAD_NEXT_UNREAD (1<<3)

enum
{


@@ 882,7 881,9 @@ typedef struct header
  /* the following are used to support collapsing threads  */
  unsigned int collapsed : 1; 	/* is this message part of a collapsed thread? */
  unsigned int limited : 1;   	/* is this message in a limited view?  */
  size_t num_hidden;          	/* number of hidden messages in this view */
  size_t num_hidden;            /* number of hidden messages in this view.
                                 * only valid for the root header, when
                                 * collapsed is set. */

  short recipient;		/* user_is_recipient()'s return value, cached */


M protos.h => protos.h +0 -1
@@ 52,7 52,6 @@ int _mutt_aside_thread (HEADER *, short, short);

#define mutt_collapse_thread(x,y) _mutt_traverse_thread (x,y,MUTT_THREAD_COLLAPSE)
#define mutt_uncollapse_thread(x,y) _mutt_traverse_thread (x,y,MUTT_THREAD_UNCOLLAPSE)
#define mutt_get_hidden(x,y)_mutt_traverse_thread (x,y,MUTT_THREAD_GET_HIDDEN)
#define mutt_thread_contains_unread(x,y) _mutt_traverse_thread (x,y,MUTT_THREAD_UNREAD)
#define mutt_thread_next_unread(x,y) _mutt_traverse_thread(x,y,MUTT_THREAD_NEXT_UNREAD)
int _mutt_traverse_thread (CONTEXT *ctx, HEADER *hdr, int flag);

M thread.c => thread.c +9 -6
@@ 1132,7 1132,6 @@ void mutt_set_virtual (CONTEXT *ctx)
      ctx->vcount++;
      ctx->vsize += cur->content->length + cur->content->offset -
        cur->content->hdr_offset + padding;
      cur->num_hidden = mutt_get_hidden (ctx, cur);
    }
  }
}


@@ 1146,7 1145,7 @@ int _mutt_traverse_thread (CONTEXT *ctx, HEADER *cur, int flag)
  int min_unread_msgno = INT_MAX, min_unread = cur->virtual;
#define CHECK_LIMIT (!ctx->pattern || cur->limited)

  if ((Sort & SORT_MASK) != SORT_THREADS && !(flag & MUTT_THREAD_GET_HIDDEN))
  if ((Sort & SORT_MASK) != SORT_THREADS)
  {
    mutt_error (_("Threading is not enabled."));
    return (cur->virtual);


@@ 1194,11 1193,13 @@ int _mutt_traverse_thread (CONTEXT *ctx, HEADER *cur, int flag)
  {
    /* return value depends on action requested */
    if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
    {
      if (roothdr)
        roothdr->num_hidden = num_hidden;
      return (final);
    }
    else if (flag & MUTT_THREAD_UNREAD)
      return ((old && new) ? new : (old ? old : new));
    else if (flag & MUTT_THREAD_GET_HIDDEN)
      return (num_hidden);
    else if (flag & MUTT_THREAD_NEXT_UNREAD)
      return (min_unread);
  }


@@ 1280,11 1281,13 @@ int _mutt_traverse_thread (CONTEXT *ctx, HEADER *cur, int flag)

  /* return value depends on action requested */
  if (flag & (MUTT_THREAD_COLLAPSE | MUTT_THREAD_UNCOLLAPSE))
  {
    if (roothdr)
      roothdr->num_hidden = num_hidden + 1;
    return (final);
  }
  else if (flag & MUTT_THREAD_UNREAD)
    return ((old && new) ? new : (old ? old : new));
  else if (flag & MUTT_THREAD_GET_HIDDEN)
    return (num_hidden+1);
  else if (flag & MUTT_THREAD_NEXT_UNREAD)
    return (min_unread);