From 3b24c8a0a4782d1118520005376ed0b5c60d43d3 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Tue, 21 Jul 2020 03:40:28 +0200 Subject: [PATCH] sortlistmodel: Use GtkSortKeys This massively speeds up sorting with expensive sort functions that it's the most worthwhile optimization of this whole branch. It's slower for simple sort functions though. It's also quite a lot slower when the model doesn't support sort keys (like GtkCustomSorter), but all the other sorters do support keys. Of course, this depends on the number of items in the model - the number of comparisons scales O(N * log N) while the overhead for key handling scales O(N). So as the log N part grows, generating keys gets more and more beneficial. Benchmarks: initial sort of a GFileInfo model with display-name keys items keys 8,000 items 715ms 50ms 64,000 items --- 554ms initial sort of a GFileInfo model with complex keys items keys 64,000 items 340ms 295ms 128,000 items 641ms 605ms removing half a GFileInfo model with display-name keys (no comparisons, just key freeing overhead of a complex sorter) items keys 512,000 items 14ms 21ms 2,048,000 items 40ms 62ms removing half a GFileInfo model with complex keys (no comparisons, just key freeing overhead of a complex sorter) items keys 512,000 items 90ms 237ms 2,048,000 items 247ms 601ms --- gtk/gtksortlistmodel.c | 134 +++++++++++++++++++++++++++++++---------- 1 file changed, 102 insertions(+), 32 deletions(-) diff --git a/gtk/gtksortlistmodel.c b/gtk/gtksortlistmodel.c index aa09017941..55c052d577 100644 --- a/gtk/gtksortlistmodel.c +++ b/gtk/gtksortlistmodel.c @@ -23,6 +23,7 @@ #include "gtkintl.h" #include "gtkprivate.h" +#include "gtksorterprivate.h" #include "gtktimsortprivate.h" /* The maximum amount of items to merge for a single merge step @@ -86,7 +87,9 @@ struct _GtkSortListModel guint sort_cb; /* 0 or current ongoing sort callback */ guint n_items; - gpointer *keys; + GtkSortKeys *sort_keys; + gsize key_size; + gpointer keys; gpointer *positions; }; @@ -101,7 +104,7 @@ static guint pos_from_key (GtkSortListModel *self, gpointer key) { - guint pos = (gpointer *) key - self->keys; + guint pos = ((char *) key - (char *) self->keys) / self->key_size; g_assert (pos < self->n_items); @@ -112,7 +115,7 @@ static gpointer key_from_pos (GtkSortListModel *self, guint pos) { - return self->keys + pos; + return (char *) self->keys + self->key_size * pos; } static GType @@ -248,10 +251,11 @@ sort_func (gconstpointer a, gconstpointer b, gpointer data) { - gpointer **sa = (gpointer **) a; - gpointer **sb = (gpointer **) b; + gpointer *sa = (gpointer *) a; + gpointer *sb = (gpointer *) b; + int result; - return gtk_sorter_compare (data, **sa, **sb); + return gtk_sort_keys_compare (data, *sa, *sb); } static gboolean @@ -265,7 +269,7 @@ gtk_sort_list_model_start_sorting (GtkSortListModel *self, self->n_items, sizeof (gpointer), sort_func, - self->sorter); + self->sort_keys); if (runs) gtk_tim_sort_set_runs (&self->sort, runs); if (self->incremental) @@ -291,16 +295,31 @@ gtk_sort_list_model_finish_sorting (GtkSortListModel *self, gtk_sort_list_model_stop_sorting (self, NULL); } +static void +gtk_sort_list_model_clear_keys (GtkSortListModel *self) +{ + + if (gtk_sort_keys_needs_clear_key (self->sort_keys)) + { + guint i; + + for (i = 0; i < self->n_items; i++) + gtk_sort_keys_clear_key (self->sort_keys, key_from_pos (self, i)); + } + + g_clear_pointer (&self->keys, g_free); + g_clear_pointer (&self->sort_keys, gtk_sort_keys_unref); + self->key_size = 0; +} + static void gtk_sort_list_model_clear_items (GtkSortListModel *self, guint *pos, guint *n_items) { - guint i; - gtk_sort_list_model_stop_sorting (self, NULL); - if (self->positions == NULL) + if (self->sort_keys == NULL) { if (pos || n_items) *pos = *n_items = 0; @@ -330,9 +349,8 @@ gtk_sort_list_model_clear_items (GtkSortListModel *self, } g_clear_pointer (&self->positions, g_free); - for (i = 0; i < self->n_items; i++) - g_object_unref (self->keys[i]); - g_clear_pointer (&self->keys, g_free); + + gtk_sort_list_model_clear_keys (self); } static gboolean @@ -343,6 +361,26 @@ gtk_sort_list_model_should_sort (GtkSortListModel *self) gtk_sorter_get_order (self->sorter) != GTK_SORTER_ORDER_NONE; } +static void +gtk_sort_list_model_create_keys (GtkSortListModel *self) +{ + guint i; + + g_assert (self->keys == NULL); + g_assert (self->sort_keys == NULL); + g_assert (self->key_size == 0); + + self->sort_keys = gtk_sorter_get_keys (self->sorter); + self->key_size = self->sort_keys->key_size; + self->keys = g_malloc_n (self->n_items, self->key_size); + for (i = 0; i < self->n_items; i++) + { + gpointer item = g_list_model_get_item (self->model, i); + gtk_sort_keys_init_key (self->sort_keys, item, key_from_pos (self, i)); + g_object_unref (item); + } +} + static void gtk_sort_list_model_create_items (GtkSortListModel *self) { @@ -351,13 +389,14 @@ gtk_sort_list_model_create_items (GtkSortListModel *self) if (!gtk_sort_list_model_should_sort (self)) return; + g_assert (self->sort_keys == NULL); + self->positions = g_new (gpointer, self->n_items); - self->keys = g_new (gpointer, self->n_items); + + gtk_sort_list_model_create_keys (self); + for (i = 0; i < self->n_items; i++) - { - self->keys[i] = g_list_model_get_item (self->model, i); - self->positions[i] = key_from_pos (self, i); - } + self->positions[i] = key_from_pos (self, i); } /* This realloc()s the arrays but does not set the added values. */ @@ -381,27 +420,30 @@ gtk_sort_list_model_update_items (GtkSortListModel *self, /* first, move the keys over */ old_keys = self->keys; for (i = position; i < position + removed; i++) - g_object_unref (self->keys[i]); + { + gtk_sort_keys_clear_key (self->sort_keys, key_from_pos (self, i)); + } + if (removed > added) { - memmove (self->keys + position + removed, - self->keys + position + added, - sizeof (gpointer) * (n_items - position - removed)); - self->keys = g_renew (gpointer, self->keys, n_items - removed + added); + memmove (key_from_pos (self, position + added), + key_from_pos (self, position + removed), + self->key_size * (n_items - position - removed)); + self->keys = g_realloc_n (self->keys, n_items - removed + added, self->key_size); } else if (removed < added) { - self->keys = g_renew (gpointer, self->keys, n_items - removed + added); - memmove (self->keys + position + removed, - self->keys + position + added, - sizeof (gpointer) * (n_items - position - removed)); + self->keys = g_realloc_n (self->keys, n_items - removed + added, self->key_size); + memmove (key_from_pos (self, position + added), + key_from_pos (self, position + removed), + self->key_size * (n_items - position - removed)); } /* then, update the positions */ valid = 0; for (i = 0; i < n_items; i++) { - guint pos = (gpointer *) self->positions[i] - old_keys; + guint pos = ((char *) self->positions[i] - (char *) old_keys) / self->key_size; if (pos >= position + removed) pos = pos - removed + added; @@ -426,7 +468,9 @@ gtk_sort_list_model_update_items (GtkSortListModel *self, for (i = 0; i < added; i++) { - self->keys[position + i] = g_list_model_get_item (self->model, position + i); + gpointer item = g_list_model_get_item (self->model, position + i); + gtk_sort_keys_init_key (self->sort_keys, item, key_from_pos (self, position + i)); + g_object_unref (item); self->positions[self->n_items - added + i] = key_from_pos (self, position + i); } @@ -559,11 +603,37 @@ gtk_sort_list_model_sorter_changed_cb (GtkSorter *sorter, if (gtk_sort_list_model_should_sort (self)) { - if (self->positions == NULL) - gtk_sort_list_model_create_items (self); - gtk_sort_list_model_stop_sorting (self, NULL); + if (self->sort_keys == NULL) + { + gtk_sort_list_model_create_items (self); + } + else + { + GtkSortKeys *new_keys = gtk_sorter_get_keys (sorter); + + if (!gtk_sort_keys_is_compatible (new_keys, self->sort_keys)) + { + char *old_keys = self->keys; + gsize old_key_size = self->key_size; + guint i; + + gtk_sort_list_model_clear_keys (self); + gtk_sort_list_model_create_keys (self); + + for (i = 0; i < self->n_items; i++) + self->positions[i] = key_from_pos (self, ((char *) self->positions[i] - old_keys) / old_key_size); + + gtk_sort_keys_unref (new_keys); + } + else + { + gtk_sort_keys_unref (self->sort_keys); + self->sort_keys = new_keys; + } + } + if (gtk_sort_list_model_start_sorting (self, NULL)) pos = n_items = 0; else