Le 19/12/2010 16:05, Colomban Wendling a écrit : > [...] > > 5) and finally, > 0008-Update-the-symbol-list-rather-than-clearing-and-re-b.patch: this is > the huge part of the thing: it re-implements tag store updating to avoid > clearing and re-filling the list, rather updating the rows when > possible. Apart avoiding flickering, this also keeps user's selection > and folding :) > As you might read at the top of the patch, this implementation is > probably not the better possible, but avoids lot of refactoring in a > code I'm not sure to understand perfectly well (add_tree_tag()) -- note > that I suggest another implementation in the patch's comment, and I have > a working sample (but not with the symbol list...) if somebody's interested. > About this patch, there is a question (as a FIXME in the patch): why did > you detached the tag store from the tag view before updating it? Was it > for performance reasons or what? I dropped this since it (obviously) led > to flickering, and it seems to work well, but I'm wondering. I found a few days ago that my first implementation had serious performance problems with very large token trees [1]. So, there is a patch to improve it (it's still not the implementation I suggested originally, but simply an improvement of the current one), and it is now significantly better [2].
So here's the patch (yet another one :D). Cheers, Colomban [1] e.g. a 8000+ lines Vala VAPI file with 6000+ tags took 18s (!!) to update, and a 64000+ lines C file with 9000+ tags took about 10s to update [2] same files now takes about 250ms for the Vala file and the C one 1600ms (not that good, but it's 64k lines...). Note that with unmodified version of Geany, the same thing takes respectively 400ms and 2700ms, so there's a benefit.
>From e7373c941198f2591c4ff436d1267f42313a7d61 Mon Sep 17 00:00:00 2001 From: Colomban Wendling <[email protected]> Date: Thu, 17 Feb 2011 19:40:51 +0100 Subject: [PATCH] Improve performances of symbol list updating Rather than walking the whole tree for each tag to find a possibly corresponding row, use a hash table as cache. This is a very significant improvement on large files with many tags, reducing for example to about 170ms an update that took more than 18s before. Also fix merging of tags with same name and scope (probably unlikely to exist in real-world files, but that tagmanager extract them correctly and they used to display correctly too). --- src/symbols.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/symbols.c b/src/symbols.c index 144ea69..255ac01 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -1161,30 +1161,89 @@ static GdkPixbuf *get_child_icon(GtkTreeStore *tree_store, GtkTreeIter *parent) } -static gboolean find_iter(GtkTreeStore *store, GtkTreeIter *iter, const TMTag *tag) +static gboolean tag_equal(gconstpointer v1, gconstpointer v2) +{ + const TMTag *t1 = v1; + const TMTag *t2 = v2; + + return (t1->type == t2->type && strcmp(t1->name, t2->name) == 0 && + utils_str_equal(t1->atts.entry.scope, t2->atts.entry.scope)); +} + + +/* inspired from g_str_hash() */ +static guint tag_hash(gconstpointer v) +{ + const TMTag *tag = v; + const signed char *p; + guint32 h = 5381; + + h = (h << 5) + h + tag->type; + for (p = tag->name; *p != '\0'; p++) + h = (h << 5) + h + *p; + if (tag->atts.entry.scope) + { + for (p = tag->atts.entry.scope; *p != '\0'; p++) + h = (h << 5) + h + *p; + } + + return h; +} + + +static GHashTable *build_iter_table(GtkTreeStore *store) { GtkTreeModel *model = GTK_TREE_MODEL(store); - gboolean found = FALSE; + GHashTable *table; + GtkTreeIter iter; - if (!gtk_tree_model_get_iter_first(model, iter)) - return FALSE; + table = g_hash_table_new_full(tag_hash, tag_equal, + (GDestroyNotify)tm_tag_unref, (GDestroyNotify)gtk_tree_path_free); + + if (!gtk_tree_model_get_iter_first(model, &iter)) + return table; do { - TMTag *candidate; + TMTag *tag; - gtk_tree_model_get(model, iter, SYMBOLS_COLUMN_TAG, &candidate, -1); - found = (candidate && candidate->type == tag->type && - utils_str_equal(candidate->name, tag->name) && - utils_str_equal(candidate->atts.entry.scope, tag->atts.entry.scope)); - tm_tag_unref(candidate); + gtk_tree_model_get(model, &iter, SYMBOLS_COLUMN_TAG, &tag, -1); + if (tag) + g_hash_table_insert(table, tag, gtk_tree_model_get_path(model, &iter)); } - while (!found && next_iter(model, iter)); + while (next_iter(model, &iter)); + + return table; +} - return found; + +static gboolean find_iter(GtkTreeStore *store, GHashTable *iter_hash, const TMTag *tag, + GtkTreeIter *iter) +{ + GtkTreePath *path; + + path = g_hash_table_lookup(iter_hash, tag); + if (path) + { + GtkTreeIter tmp; + gboolean valid; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &tmp, path)) + return FALSE; + gtk_tree_model_get(GTK_TREE_MODEL(store), &tmp, SYMBOLS_COLUMN_VALID, &valid, -1); + /* if the row is valid it has already been updated, so it's simply a duplicate */ + if (!valid) + { + *iter = tmp; + return TRUE; + } + } + + return FALSE; } -static void add_tree_tag(GeanyDocument *doc, const TMTag *tag, GHashTable *parent_hash) +static void add_tree_tag(GeanyDocument *doc, const TMTag *tag, GHashTable *parent_hash, + GHashTable *iter_hash) { filetype_id ft_id = doc->file_type->id; GtkTreeStore *tree_store = doc->priv->tag_store; @@ -1226,7 +1285,7 @@ static void add_tree_tag(GeanyDocument *doc, const TMTag *tag, GHashTable *paren child = new_iter; } - if (!find_iter(tree_store, child, tag)) + if (!find_iter(tree_store, iter_hash, tag, child)) { gboolean expand = FALSE; @@ -1271,9 +1330,11 @@ static void add_tree_tags(GeanyDocument *doc, const GList *tags) { const GList *item; GHashTable *parent_hash; + GHashTable *iter_hash; /* Create a hash table "parent_tag_name":(GtkTreeIter*) */ parent_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); + iter_hash = build_iter_table(doc->priv->tag_store); /* find and store all parent names in the hash table */ for (item = tags; item; item = g_list_next(item)) @@ -1288,8 +1349,9 @@ static void add_tree_tags(GeanyDocument *doc, const GList *tags) { const TMTag *tag = item->data; - add_tree_tag(doc, tag, parent_hash); + add_tree_tag(doc, tag, parent_hash, iter_hash); } + g_hash_table_destroy(iter_hash); g_hash_table_destroy(parent_hash); } -- 1.7.4.1
_______________________________________________ Geany-devel mailing list [email protected] http://lists.uvena.de/cgi-bin/mailman/listinfo/geany-devel
