Re: ideas on improving the performance of gtk_tree_view
- From: markku vire iki fi
- To: Federico Mena Quintero <federico ximian com>
- Cc: gtk-devel-list gnome org
- Subject: Re: ideas on improving the performance of gtk_tree_view
- Date: Mon, 26 Mar 2007 00:18:07 +0300
Hi,
Lainaus Federico Mena Quintero <federico ximian com>:
> Exactly. GtkListStore is implemented as a GSequence (a splay tree),
> where each node is one row in the list. Then, the data for the row's
> columns is stored in a linked list (GtkTreeDataList). You could
> reimplement GtkTreeDataList in terms of an array and get a good speedup
> for *all* uses of GtkListStore (i.e. you'd be replacing a dominant
> O(n^2) for get/set operations in the list store with a simpler O(log n)
> for splay tree lookups, plus the constant time to access a column within
> a node).
>
> Using an array instead of a linked list for GtkTreeDataList is a great
> idea, and we should definitely review a patch for that :)
I tried this idea and changed the GtkTreeDataList to be an array instead of
linked list (see the attached patch, made against svn head). The original
testcase (5000x50 model) started up faster (but was still slow), but I didn't do
any actual measurements yet (let's see tomorrow ;)
I made the following changes:
* Added dimension/index parameter to many GtkTreeDataList calls (I understood
that this API was OK to be changed). The GtkTreeDataList is now an array of
n elements instead of single list node.
* GtkListStore and GtkTreeStore needed similar patches, including:
Simpler get_value/set_value implementation, because we know that the
datalist will contain n elements and they can be indexed easily.
* Drag'n'drop code now uses simpler _gtk_tree_data_list_copy (I renamed
_gtk_tree_data_list_node_copy, since we now have more than one element).
-Markku-
Index: gtk/gtktreestore.c
===================================================================
--- gtk/gtktreestore.c (revision 17562)
+++ gtk/gtktreestore.c (working copy)
@@ -386,8 +386,11 @@
node_free (GNode *node, gpointer data)
{
if (node->data)
- _gtk_tree_data_list_free (node->data, (GType*)data);
- node->data = NULL;
+ {
+ GtkTreeStore *store = GTK_TREE_STORE(data);
+ _gtk_tree_data_list_free (node->data, store->column_headers, store->n_columns);
+ node->data = NULL;
+ }
return FALSE;
}
@@ -398,7 +401,7 @@
GtkTreeStore *tree_store = GTK_TREE_STORE (object);
g_node_traverse (tree_store->root, G_POST_ORDER, G_TRAVERSE_ALL, -1,
- node_free, tree_store->column_headers);
+ node_free, tree_store);
g_node_destroy (tree_store->root);
_gtk_tree_data_list_header_free (tree_store->sort_list);
g_free (tree_store->column_headers);
@@ -557,20 +560,17 @@
{
GtkTreeStore *tree_store = (GtkTreeStore *) tree_model;
GtkTreeDataList *list;
- gint tmp_column = column;
g_return_if_fail (column < tree_store->n_columns);
g_return_if_fail (VALID_ITER (iter, tree_store));
list = G_NODE (iter->user_data)->data;
- while (tmp_column-- > 0 && list)
- list = list->next;
-
if (list)
{
_gtk_tree_data_list_node_to_value (list,
tree_store->column_headers[column],
+ column,
value);
}
else
@@ -719,8 +719,6 @@
gboolean sort)
{
GtkTreeDataList *list;
- GtkTreeDataList *prev;
- gint old_column = column;
GValue real_value = {0, };
gboolean converted = FALSE;
gboolean retval = FALSE;
@@ -748,61 +746,23 @@
converted = TRUE;
}
- prev = list = G_NODE (iter->user_data)->data;
-
- while (list != NULL)
+ list = G_NODE (iter->user_data)->data;
+ if (G_UNLIKELY(list == NULL))
{
- if (column == 0)
- {
+ G_NODE (iter->user_data)->data = list =
+ _gtk_tree_data_list_alloc (tree_store->n_columns);
+ }
+
if (converted)
- _gtk_tree_data_list_value_to_node (list, &real_value);
+ _gtk_tree_data_list_value_to_node (list, column, &real_value);
else
- _gtk_tree_data_list_value_to_node (list, value);
+ _gtk_tree_data_list_value_to_node (list, column, value);
retval = TRUE;
if (converted)
g_value_unset (&real_value);
if (sort && GTK_TREE_STORE_IS_SORTED (tree_store))
- gtk_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE);
+ gtk_tree_store_sort_iter_changed (tree_store, iter, column, TRUE);
return retval;
- }
-
- column--;
- prev = list;
- list = list->next;
- }
-
- if (G_NODE (iter->user_data)->data == NULL)
- {
- G_NODE (iter->user_data)->data = list = _gtk_tree_data_list_alloc ();
- list->next = NULL;
- }
- else
- {
- list = prev->next = _gtk_tree_data_list_alloc ();
- list->next = NULL;
- }
-
- while (column != 0)
- {
- list->next = _gtk_tree_data_list_alloc ();
- list = list->next;
- list->next = NULL;
- column --;
- }
-
- if (converted)
- _gtk_tree_data_list_value_to_node (list, &real_value);
- else
- _gtk_tree_data_list_value_to_node (list, value);
-
- retval = TRUE;
- if (converted)
- g_value_unset (&real_value);
-
- if (sort && GTK_TREE_STORE_IS_SORTED (tree_store))
- gtk_tree_store_sort_iter_changed (tree_store, iter, old_column, TRUE);
-
- return retval;
}
/**
@@ -1013,7 +973,7 @@
if (G_NODE (iter->user_data)->data)
g_node_traverse (G_NODE (iter->user_data), G_POST_ORDER, G_TRAVERSE_ALL,
- -1, node_free, tree_store->column_headers);
+ -1, node_free, tree_store);
path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), iter);
g_node_destroy (G_NODE (iter->user_data));
@@ -1791,31 +1751,11 @@
GtkTreeIter *dest_iter)
{
GtkTreeDataList *dl = G_NODE (src_iter->user_data)->data;
- GtkTreeDataList *copy_head = NULL;
- GtkTreeDataList *copy_prev = NULL;
- GtkTreeDataList *copy_iter = NULL;
GtkTreePath *path;
- gint col;
- col = 0;
- while (dl)
- {
- copy_iter = _gtk_tree_data_list_node_copy (dl, tree_store->column_headers[col]);
+ G_NODE (dest_iter->user_data)->data = _gtk_tree_data_list_copy(dl,
+ tree_store->column_headers, tree_store->n_columns);
- if (copy_head == NULL)
- copy_head = copy_iter;
-
- if (copy_prev)
- copy_prev->next = copy_iter;
-
- copy_prev = copy_iter;
-
- dl = dl->next;
- ++col;
- }
-
- G_NODE (dest_iter->user_data)->data = copy_head;
-
path = gtk_tree_store_get_path (GTK_TREE_MODEL (tree_store), dest_iter);
gtk_tree_model_row_changed (GTK_TREE_MODEL (tree_store), path, dest_iter);
gtk_tree_path_free (path);
Index: gtk/gtktreedatalist.c
===================================================================
--- gtk/gtktreedatalist.c (revision 17562)
+++ gtk/gtktreedatalist.c (working copy)
@@ -28,38 +28,30 @@
/* node allocation
*/
GtkTreeDataList *
-_gtk_tree_data_list_alloc (void)
+_gtk_tree_data_list_alloc (gint n)
{
- GtkTreeDataList *list;
-
- list = g_slice_new0 (GtkTreeDataList);
-
- return list;
+ return g_slice_alloc0(sizeof(GtkTreeDataList) * n);
}
void
_gtk_tree_data_list_free (GtkTreeDataList *list,
- GType *column_headers)
+ GType *column_headers, gint n)
{
- GtkTreeDataList *tmp, *next;
- gint i = 0;
+ gpointer p;
+ gint i;
- tmp = list;
-
- while (tmp)
+ for (i = 0; i < n; i++)
+ if ( (p = list[i].data.v_pointer) != NULL)
{
- next = tmp->next;
if (g_type_is_a (column_headers [i], G_TYPE_STRING))
- g_free ((gchar *) tmp->data.v_pointer);
- else if (g_type_is_a (column_headers [i], G_TYPE_OBJECT) && tmp->data.v_pointer != NULL)
- g_object_unref (tmp->data.v_pointer);
- else if (g_type_is_a (column_headers [i], G_TYPE_BOXED) && tmp->data.v_pointer != NULL)
- g_boxed_free (column_headers [i], (gpointer) tmp->data.v_pointer);
-
- g_slice_free (GtkTreeDataList, tmp);
- i++;
- tmp = next;
+ g_free (p);
+ else if (g_type_is_a (column_headers [i], G_TYPE_OBJECT))
+ g_object_unref (p);
+ else if (g_type_is_a (column_headers [i], G_TYPE_BOXED))
+ g_boxed_free (column_headers [i], p);
}
+
+ g_slice_free1(sizeof(GtkTreeDataList) * n, list);
}
gboolean
@@ -118,7 +110,7 @@
}
void
_gtk_tree_data_list_node_to_value (GtkTreeDataList *list,
- GType type,
+ GType type, gint index,
GValue *value)
{
g_value_init (value, type);
@@ -126,55 +118,55 @@
switch (get_fundamental_type (type))
{
case G_TYPE_BOOLEAN:
- g_value_set_boolean (value, (gboolean) list->data.v_int);
+ g_value_set_boolean (value, (gboolean) list[index].data.v_int);
break;
case G_TYPE_CHAR:
- g_value_set_char (value, (gchar) list->data.v_char);
+ g_value_set_char (value, (gchar) list[index].data.v_char);
break;
case G_TYPE_UCHAR:
- g_value_set_uchar (value, (guchar) list->data.v_uchar);
+ g_value_set_uchar (value, (guchar) list[index].data.v_uchar);
break;
case G_TYPE_INT:
- g_value_set_int (value, (gint) list->data.v_int);
+ g_value_set_int (value, (gint) list[index].data.v_int);
break;
case G_TYPE_UINT:
- g_value_set_uint (value, (guint) list->data.v_uint);
+ g_value_set_uint (value, (guint) list[index].data.v_uint);
break;
case G_TYPE_LONG:
- g_value_set_long (value, list->data.v_long);
+ g_value_set_long (value, list[index].data.v_long);
break;
case G_TYPE_ULONG:
- g_value_set_ulong (value, list->data.v_ulong);
+ g_value_set_ulong (value, list[index].data.v_ulong);
break;
case G_TYPE_INT64:
- g_value_set_int64 (value, list->data.v_int64);
+ g_value_set_int64 (value, list[index].data.v_int64);
break;
case G_TYPE_UINT64:
- g_value_set_uint64 (value, list->data.v_uint64);
+ g_value_set_uint64 (value, list[index].data.v_uint64);
break;
case G_TYPE_ENUM:
- g_value_set_enum (value, list->data.v_int);
+ g_value_set_enum (value, list[index].data.v_int);
break;
case G_TYPE_FLAGS:
- g_value_set_flags (value, list->data.v_uint);
+ g_value_set_flags (value, list[index].data.v_uint);
break;
case G_TYPE_FLOAT:
- g_value_set_float (value, (gfloat) list->data.v_float);
+ g_value_set_float (value, (gfloat) list[index].data.v_float);
break;
case G_TYPE_DOUBLE:
- g_value_set_double (value, (gdouble) list->data.v_double);
+ g_value_set_double (value, (gdouble) list[index].data.v_double);
break;
case G_TYPE_STRING:
- g_value_set_string (value, (gchar *) list->data.v_pointer);
+ g_value_set_string (value, (gchar *) list[index].data.v_pointer);
break;
case G_TYPE_POINTER:
- g_value_set_pointer (value, (gpointer) list->data.v_pointer);
+ g_value_set_pointer (value, (gpointer) list[index].data.v_pointer);
break;
case G_TYPE_BOXED:
- g_value_set_boxed (value, (gpointer) list->data.v_pointer);
+ g_value_set_boxed (value, (gpointer) list[index].data.v_pointer);
break;
case G_TYPE_OBJECT:
- g_value_set_object (value, (GObject *) list->data.v_pointer);
+ g_value_set_object (value, (GObject *) list[index].data.v_pointer);
break;
default:
g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
@@ -184,65 +176,65 @@
void
_gtk_tree_data_list_value_to_node (GtkTreeDataList *list,
- GValue *value)
+ gint index, GValue *value)
{
switch (get_fundamental_type (G_VALUE_TYPE (value)))
{
case G_TYPE_BOOLEAN:
- list->data.v_int = g_value_get_boolean (value);
+ list[index].data.v_int = g_value_get_boolean (value);
break;
case G_TYPE_CHAR:
- list->data.v_char = g_value_get_char (value);
+ list[index].data.v_char = g_value_get_char (value);
break;
case G_TYPE_UCHAR:
- list->data.v_uchar = g_value_get_uchar (value);
+ list[index].data.v_uchar = g_value_get_uchar (value);
break;
case G_TYPE_INT:
- list->data.v_int = g_value_get_int (value);
+ list[index].data.v_int = g_value_get_int (value);
break;
case G_TYPE_UINT:
- list->data.v_uint = g_value_get_uint (value);
+ list[index].data.v_uint = g_value_get_uint (value);
break;
case G_TYPE_LONG:
- list->data.v_long = g_value_get_long (value);
+ list[index].data.v_long = g_value_get_long (value);
break;
case G_TYPE_ULONG:
- list->data.v_ulong = g_value_get_ulong (value);
+ list[index].data.v_ulong = g_value_get_ulong (value);
break;
case G_TYPE_INT64:
- list->data.v_int64 = g_value_get_int64 (value);
+ list[index].data.v_int64 = g_value_get_int64 (value);
break;
case G_TYPE_UINT64:
- list->data.v_uint64 = g_value_get_uint64 (value);
+ list[index].data.v_uint64 = g_value_get_uint64 (value);
break;
case G_TYPE_ENUM:
- list->data.v_int = g_value_get_enum (value);
+ list[index].data.v_int = g_value_get_enum (value);
break;
case G_TYPE_FLAGS:
- list->data.v_uint = g_value_get_flags (value);
+ list[index].data.v_uint = g_value_get_flags (value);
break;
case G_TYPE_POINTER:
- list->data.v_pointer = g_value_get_pointer (value);
+ list[index].data.v_pointer = g_value_get_pointer (value);
break;
case G_TYPE_FLOAT:
- list->data.v_float = g_value_get_float (value);
+ list[index].data.v_float = g_value_get_float (value);
break;
case G_TYPE_DOUBLE:
- list->data.v_double = g_value_get_double (value);
+ list[index].data.v_double = g_value_get_double (value);
break;
case G_TYPE_STRING:
- g_free (list->data.v_pointer);
- list->data.v_pointer = g_value_dup_string (value);
+ g_free (list[index].data.v_pointer);
+ list[index].data.v_pointer = g_value_dup_string (value);
break;
case G_TYPE_OBJECT:
- if (list->data.v_pointer)
- g_object_unref (list->data.v_pointer);
- list->data.v_pointer = g_value_dup_object (value);
+ if (list[index].data.v_pointer)
+ g_object_unref (list[index].data.v_pointer);
+ list[index].data.v_pointer = g_value_dup_object (value);
break;
case G_TYPE_BOXED:
- if (list->data.v_pointer)
- g_boxed_free (G_VALUE_TYPE (value), list->data.v_pointer);
- list->data.v_pointer = g_value_dup_boxed (value);
+ if (list[index].data.v_pointer)
+ g_boxed_free (G_VALUE_TYPE (value), list[index].data.v_pointer);
+ list[index].data.v_pointer = g_value_dup_boxed (value);
break;
default:
g_warning ("%s: Unsupported type (%s) stored.", G_STRLOC, g_type_name (G_VALUE_TYPE (value)));
@@ -251,17 +243,18 @@
}
GtkTreeDataList *
-_gtk_tree_data_list_node_copy (GtkTreeDataList *list,
- GType type)
+_gtk_tree_data_list_copy (GtkTreeDataList *list,
+ GType *types, gint n)
{
GtkTreeDataList *new_list;
-
+ gint i;
+
g_return_val_if_fail (list != NULL, NULL);
- new_list = _gtk_tree_data_list_alloc ();
- new_list->next = NULL;
-
- switch (get_fundamental_type (type))
+ new_list = _gtk_tree_data_list_alloc (n);
+
+ for (i = 0; i < n; i++)
+ switch (get_fundamental_type (types[i]))
{
case G_TYPE_BOOLEAN:
case G_TYPE_CHAR:
@@ -277,25 +270,25 @@
case G_TYPE_POINTER:
case G_TYPE_FLOAT:
case G_TYPE_DOUBLE:
- new_list->data = list->data;
+ new_list[i].data = list[i].data;
break;
case G_TYPE_STRING:
- new_list->data.v_pointer = g_strdup (list->data.v_pointer);
+ new_list[i].data.v_pointer = g_strdup (list[i].data.v_pointer);
break;
case G_TYPE_OBJECT:
case G_TYPE_INTERFACE:
- new_list->data.v_pointer = list->data.v_pointer;
- if (new_list->data.v_pointer)
- g_object_ref (new_list->data.v_pointer);
+ new_list[i].data.v_pointer = list[i].data.v_pointer;
+ if (new_list[i].data.v_pointer)
+ g_object_ref (new_list[i].data.v_pointer);
break;
case G_TYPE_BOXED:
- if (list->data.v_pointer)
- new_list->data.v_pointer = g_boxed_copy (type, list->data.v_pointer);
+ if (list[i].data.v_pointer)
+ new_list[i].data.v_pointer = g_boxed_copy (types[i], list[i].data.v_pointer);
else
- new_list->data.v_pointer = NULL;
+ new_list[i].data.v_pointer = NULL;
break;
default:
- g_warning ("Unsupported node type (%s) copied.", g_type_name (type));
+ g_warning ("Unsupported node type (%s) copied.", g_type_name (types[i]));
break;
}
Index: gtk/gtkliststore.c
===================================================================
--- gtk/gtkliststore.c (revision 17562)
+++ gtk/gtkliststore.c (working copy)
@@ -455,21 +455,18 @@
{
GtkListStore *list_store = (GtkListStore *) tree_model;
GtkTreeDataList *list;
- gint tmp_column = column;
g_return_if_fail (column < list_store->n_columns);
g_return_if_fail (VALID_ITER (iter, list_store));
list = g_sequence_get (iter->user_data);
- while (tmp_column-- > 0 && list)
- list = list->next;
-
if (list == NULL)
g_value_init (value, list_store->column_headers[column]);
else
_gtk_tree_data_list_node_to_value (list,
list_store->column_headers[column],
+ column,
value);
}
@@ -564,8 +561,6 @@
gboolean sort)
{
GtkTreeDataList *list;
- GtkTreeDataList *prev;
- gint old_column = column;
GValue real_value = {0, };
gboolean converted = FALSE;
gboolean retval = FALSE;
@@ -593,62 +588,24 @@
converted = TRUE;
}
- prev = list = g_sequence_get (iter->user_data);
+ list = g_sequence_get (iter->user_data);
+
+ if (G_UNLIKELY(list == NULL))
+ {
+ list = _gtk_tree_data_list_alloc(list_store->n_columns);
+ g_sequence_set (iter->user_data, list);
+ }
- while (list != NULL)
- {
- if (column == 0)
- {
- if (converted)
- _gtk_tree_data_list_value_to_node (list, &real_value);
- else
- _gtk_tree_data_list_value_to_node (list, value);
- retval = TRUE;
- if (converted)
- g_value_unset (&real_value);
- if (sort && GTK_LIST_STORE_IS_SORTED (list_store))
- gtk_list_store_sort_iter_changed (list_store, iter, old_column);
- return retval;
- }
-
- column--;
- prev = list;
- list = list->next;
- }
-
- if (g_sequence_get (iter->user_data) == NULL)
- {
- list = _gtk_tree_data_list_alloc();
- g_sequence_set (iter->user_data, list);
- list->next = NULL;
- }
- else
- {
- list = prev->next = _gtk_tree_data_list_alloc ();
- list->next = NULL;
- }
-
- while (column != 0)
- {
- list->next = _gtk_tree_data_list_alloc ();
- list = list->next;
- list->next = NULL;
- column --;
- }
-
if (converted)
- _gtk_tree_data_list_value_to_node (list, &real_value);
+ _gtk_tree_data_list_value_to_node (list, column, &real_value);
else
- _gtk_tree_data_list_value_to_node (list, value);
+ _gtk_tree_data_list_value_to_node (list, column, value);
- retval = TRUE;
- if (converted)
+ if (converted)
g_value_unset (&real_value);
-
if (sort && GTK_LIST_STORE_IS_SORTED (list_store))
- gtk_list_store_sort_iter_changed (list_store, iter, old_column);
-
- return retval;
+ gtk_list_store_sort_iter_changed (list_store, iter, column);
+ return TRUE;
}
@@ -857,7 +814,8 @@
ptr = iter->user_data;
next = g_sequence_iter_next (ptr);
- _gtk_tree_data_list_free (g_sequence_get (ptr), list_store->column_headers);
+ _gtk_tree_data_list_free (g_sequence_get (ptr),
+ list_store->column_headers, list_store->n_columns);
g_sequence_remove (iter->user_data);
list_store->length--;
@@ -1205,32 +1163,11 @@
if (retval)
{
GtkTreeDataList *dl = g_sequence_get (src_iter.user_data);
- GtkTreeDataList *copy_head = NULL;
- GtkTreeDataList *copy_prev = NULL;
- GtkTreeDataList *copy_iter = NULL;
GtkTreePath *path;
- gint col;
- col = 0;
- while (dl)
- {
- copy_iter = _gtk_tree_data_list_node_copy (dl,
- list_store->column_headers[col]);
-
- if (copy_head == NULL)
- copy_head = copy_iter;
-
- if (copy_prev)
- copy_prev->next = copy_iter;
-
- copy_prev = copy_iter;
-
- dl = dl->next;
- ++col;
- }
-
dest_iter.stamp = list_store->stamp;
- g_sequence_set (dest_iter.user_data, copy_head);
+ g_sequence_set (dest_iter.user_data, _gtk_tree_data_list_copy(dl,
+ list_store->column_headers, list_store->n_columns));
path = gtk_list_store_get_path (tree_model, &dest_iter);
gtk_tree_model_row_changed (tree_model, path, &dest_iter);
Index: gtk/gtktreedatalist.h
===================================================================
--- gtk/gtktreedatalist.h (revision 17562)
+++ gtk/gtktreedatalist.h (working copy)
@@ -28,8 +28,6 @@
typedef struct _GtkTreeDataList GtkTreeDataList;
struct _GtkTreeDataList
{
- GtkTreeDataList *next;
-
union {
gint v_int;
gint8 v_char;
@@ -53,18 +51,18 @@
GtkDestroyNotify destroy;
} GtkTreeDataSortHeader;
-GtkTreeDataList *_gtk_tree_data_list_alloc (void);
+GtkTreeDataList *_gtk_tree_data_list_alloc (gint n);
void _gtk_tree_data_list_free (GtkTreeDataList *list,
- GType *column_headers);
+ GType *column_headers, gint n);
gboolean _gtk_tree_data_list_check_type (GType type);
void _gtk_tree_data_list_node_to_value (GtkTreeDataList *list,
- GType type,
+ GType type, gint index,
GValue *value);
void _gtk_tree_data_list_value_to_node (GtkTreeDataList *list,
- GValue *value);
+ gint index, GValue *value);
-GtkTreeDataList *_gtk_tree_data_list_node_copy (GtkTreeDataList *list,
- GType type);
+GtkTreeDataList *_gtk_tree_data_list_copy (GtkTreeDataList *list,
+ GType *types, gint n);
/* Header code */
gint _gtk_tree_data_list_compare_func (GtkTreeModel *model,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]