mirror of https://github.com/djcb/mu.git
* 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:
parent
7fe594fb2a
commit
e483291a1d
|
@ -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
|
G_GNUC_UNUSED static gboolean
|
||||||
check_dup (MuContainer *c, GHashTable *hash)
|
check_dup (MuContainer *c, GHashTable *hash)
|
||||||
|
@ -131,7 +122,21 @@ mu_container_append_siblings (MuContainer *c, MuContainer *sibling)
|
||||||
/* assert_no_duplicates (c); */
|
/* assert_no_duplicates (c); */
|
||||||
|
|
||||||
set_parent (sibling, c->parent);
|
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); */
|
/* assert_no_duplicates (c); */
|
||||||
|
|
||||||
|
@ -158,6 +163,13 @@ mu_container_remove_sibling (MuContainer *c, MuContainer *sibling)
|
||||||
prev = cur;
|
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;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +213,8 @@ typedef void (*MuContainerPathForeachFunc) (MuContainer*, gpointer, Path*);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mu_container_path_foreach_real (MuContainer *c, guint level, Path *path,
|
mu_container_path_foreach_real (MuContainer *c, guint level, Path *path,
|
||||||
MuContainerPathForeachFunc func, gpointer user_data)
|
MuContainerPathForeachFunc func,
|
||||||
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
if (!c)
|
if (!c)
|
||||||
return;
|
return;
|
||||||
|
@ -210,7 +223,8 @@ mu_container_path_foreach_real (MuContainer *c, guint level, Path *path,
|
||||||
func (c, user_data, path);
|
func (c, user_data, path);
|
||||||
|
|
||||||
/* children */
|
/* 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 */
|
/* siblings */
|
||||||
mu_container_path_foreach_real (c->next, level, path, func, user_data);
|
mu_container_path_foreach_real (c->next, level, path, func, user_data);
|
||||||
|
|
|
@ -29,8 +29,7 @@ enum _MuContainerFlag {
|
||||||
MU_CONTAINER_FLAG_SPLICE = 1 << 1,
|
MU_CONTAINER_FLAG_SPLICE = 1 << 1,
|
||||||
MU_CONTAINER_FLAG_DUP = 1 << 2
|
MU_CONTAINER_FLAG_DUP = 1 << 2
|
||||||
};
|
};
|
||||||
|
typedef enum _MuContainerFlag MuContainerFlag;
|
||||||
typedef guint8 MuContainerFlag;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MuContainer data structure, as seen in JWZs document:
|
* MuContainer data structure, as seen in JWZs document:
|
||||||
|
@ -38,10 +37,21 @@ typedef guint8 MuContainerFlag;
|
||||||
*/
|
*/
|
||||||
struct _MuContainer {
|
struct _MuContainer {
|
||||||
struct _MuContainer *parent, *child, *next;
|
struct _MuContainer *parent, *child, *next;
|
||||||
MuContainerFlag flags;
|
|
||||||
MuMsg *msg;
|
/* note: we cache the last of the string of next->next->...
|
||||||
guint docid;
|
* `mu_container_append_siblings' shows up high in the
|
||||||
const char* msgid;
|
* 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;
|
typedef struct _MuContainer MuContainer;
|
||||||
|
|
||||||
|
@ -177,7 +187,8 @@ typedef int (*MuContainerCmpFunc) (MuContainer *c1, MuContainer *c2,
|
||||||
*
|
*
|
||||||
* @return a sorted container
|
* @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);
|
gpointer user_data);
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue