* lib/mu-container: remove some O(n*n) behavior from threading

mu_container_append_siblings was showing up high in profiles as it has to
  walk chains of next->next->next->... pointers to find the last one. we now
  cache the last link in the chain. for listing ~ 23K messages, this saves
  about 20%.
This commit is contained in:
djcb 2012-09-17 17:45:59 +03:00
parent 7fe594fb2a
commit e483291a1d
2 changed files with 44 additions and 19 deletions

View File

@ -80,15 +80,6 @@ set_parent (MuContainer *c, MuContainer *parent)
}
}
static MuContainer*
find_last (MuContainer *c)
{
while (c && c->next)
c = c->next;
return c;
}
G_GNUC_UNUSED static gboolean
check_dup (MuContainer *c, GHashTable *hash)
@ -131,7 +122,21 @@ mu_container_append_siblings (MuContainer *c, MuContainer *sibling)
/* assert_no_duplicates (c); */
set_parent (sibling, c->parent);
(find_last(c))->next = sibling;
/* find the last sibling and append; first we try our cache
* 'last', otherwise we need to walk the chain. We use a
* cached last as to avoid walking the chain (which is
* O(n*n)) */
if (c->last)
c->last->next = sibling;
else {
/* no 'last' cached, so walk the chain */
MuContainer *c2;
for (c2 = c; c2 && c2->next; c2 = c2->next);
c2->next = sibling;
}
/* update the cached last */
c->last = sibling->last ? sibling->last : sibling;
/* assert_no_duplicates (c); */
@ -158,6 +163,13 @@ mu_container_remove_sibling (MuContainer *c, MuContainer *sibling)
prev = cur;
}
/* unset the cached last; it's not valid anymore
*
* TODO: we could actually do a better job updating last
* rather than invalidating it. */
if (c)
c->last = NULL;
return c;
}
@ -201,7 +213,8 @@ typedef void (*MuContainerPathForeachFunc) (MuContainer*, gpointer, Path*);
static void
mu_container_path_foreach_real (MuContainer *c, guint level, Path *path,
MuContainerPathForeachFunc func, gpointer user_data)
MuContainerPathForeachFunc func,
gpointer user_data)
{
if (!c)
return;
@ -210,7 +223,8 @@ mu_container_path_foreach_real (MuContainer *c, guint level, Path *path,
func (c, user_data, path);
/* children */
mu_container_path_foreach_real (c->child, level + 1, path, func, user_data);
mu_container_path_foreach_real (c->child, level + 1, path,
func, user_data);
/* siblings */
mu_container_path_foreach_real (c->next, level, path, func, user_data);

View File

@ -29,8 +29,7 @@ enum _MuContainerFlag {
MU_CONTAINER_FLAG_SPLICE = 1 << 1,
MU_CONTAINER_FLAG_DUP = 1 << 2
};
typedef guint8 MuContainerFlag;
typedef enum _MuContainerFlag MuContainerFlag;
/*
* MuContainer data structure, as seen in JWZs document:
@ -38,10 +37,21 @@ typedef guint8 MuContainerFlag;
*/
struct _MuContainer {
struct _MuContainer *parent, *child, *next;
MuContainerFlag flags;
MuMsg *msg;
guint docid;
const char* msgid;
/* note: we cache the last of the string of next->next->...
* `mu_container_append_siblings' shows up high in the
* profiles since it needs to walk to the end, and this give
* O(n*n) behavior.
* */
struct _MuContainer *last;
MuMsg *msg;
const char *msgid;
unsigned docid;
MuContainerFlag flags;
};
typedef struct _MuContainer MuContainer;
@ -177,7 +187,8 @@ typedef int (*MuContainerCmpFunc) (MuContainer *c1, MuContainer *c2,
*
* @return a sorted container
*/
MuContainer* mu_container_sort (MuContainer *c, MuMsgFieldId mfid, gboolean revert,
MuContainer* mu_container_sort (MuContainer *c, MuMsgFieldId mfid,
gboolean revert,
gpointer user_data);