From 1c4f6da1f707581aef0e7166b0d6f568ea37ab3c Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 22 May 2022 07:37:01 -0400 Subject: [PATCH 1/4] Add a finalization performance test --- tests/gobject/performance.c | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/gobject/performance.c b/tests/gobject/performance.c index 5208172bd..e906f79f7 100644 --- a/tests/gobject/performance.c +++ b/tests/gobject/performance.c @@ -521,6 +521,48 @@ test_construction_teardown (PerformanceTest *test, g_free (data); } +static void +test_finalization_init (PerformanceTest *test, + gpointer _data, + double count_factor) +{ + struct ConstructionTest *data = _data; + int n; + + n = NUM_OBJECT_TO_CONSTRUCT * count_factor; + if (data->n_objects != n) + { + data->n_objects = n; + data->objects = g_new (GObject *, n); + } + + for (int i = 0; i < data->n_objects; i++) + { + data->objects[i] = g_object_new (data->type, NULL); + } +} + +static void +test_finalization_run (PerformanceTest *test, + gpointer _data) +{ + struct ConstructionTest *data = _data; + GObject **objects = data->objects; + int i, n_objects; + + n_objects = data->n_objects; + for (i = 0; i < n_objects; i++) + { + g_object_unref (objects[i]); + } +} + +static void +test_finalization_finish (PerformanceTest *test, + gpointer _data) +{ +} + static void test_construction_print_result (PerformanceTest *test, gpointer _data, @@ -532,6 +574,17 @@ test_construction_print_result (PerformanceTest *test, data->n_objects / (time * 1000000)); } +static void +test_finalization_print_result (PerformanceTest *test, + gpointer _data, + double time) +{ + struct ConstructionTest *data = _data; + + g_print ("Millions of finalized objects per second: %.3f\n", + data->n_objects / (time * 1000000)); +} + /************************************************************* * Test runtime type check performance *************************************************************/ @@ -890,6 +943,16 @@ static PerformanceTest tests[] = { test_construction_teardown, test_construction_print_result }, + { + "finalization", + simple_object_get_type, + test_construction_setup, + test_finalization_init, + test_finalization_run, + test_finalization_finish, + test_construction_teardown, + test_finalization_print_result + }, { "type-check", NULL, From 0415bf94127c692094facaba6ef349e6809617e4 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 22 May 2022 06:47:55 -0400 Subject: [PATCH 2/4] Add g_datalist_id_remove_multiple This is more efficient than calling g_datalist_id_remove() multiple times in a row, since it only takes the locks once. Allow up to 16 keys to be removed in one go. That is enough for the use we have in GObject, and it avoids any danger of blowing the stack. --- docs/reference/glib/glib-sections.txt | 1 + glib/gdataset.c | 103 ++++++++++++++++++++++++++ glib/gdataset.h | 4 + 3 files changed, 108 insertions(+) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 2932313cd..bacf38684 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -3220,6 +3220,7 @@ g_datalist_id_set_data_full g_datalist_id_get_data g_datalist_id_remove_data g_datalist_id_remove_no_notify +g_datalist_id_remove_multiple GDuplicateFunc g_datalist_id_dup_data g_datalist_id_replace_data diff --git a/glib/gdataset.c b/glib/gdataset.c index 758c032ab..6b78d2e33 100644 --- a/glib/gdataset.c +++ b/glib/gdataset.c @@ -46,6 +46,7 @@ #include "gtestutils.h" #include "gthread.h" #include "glib_trace.h" +#include "galloca.h" /** * SECTION:datasets @@ -487,6 +488,85 @@ g_data_set_internal (GData **datalist, } +static inline void +g_data_remove_internal (GData **datalist, + GQuark *keys, + gsize n_keys) +{ + GData *d; + + g_datalist_lock (datalist); + + d = G_DATALIST_GET_POINTER (datalist); + + if (d) + { + GDataElt *old, *data, *data_end; + gsize found_keys; + + old = g_newa (GDataElt, n_keys); + + data = d->data; + data_end = data + d->len; + found_keys = 0; + + while (data < data_end && found_keys < n_keys) + { + gboolean remove = FALSE; + + for (gsize i = 0; i < n_keys; i++) + { + if (data->key == keys[i]) + { + remove = TRUE; + break; + } + } + + if (remove) + { + old[found_keys] = *data; + found_keys++; + + if (data < data_end) + { + data_end--; + *data = *data_end; + } + + d->len--; + + /* We don't bother to shrink, but if all data are now gone + * we at least free the memory + */ + if (d->len == 0) + { + G_DATALIST_SET_POINTER (datalist, NULL); + g_free (d); + break; + } + } + + data++; + } + + if (found_keys > 0) + { + g_datalist_unlock (datalist); + + for (gsize i = 0; i < found_keys; i++) + { + if (old[i].destroy) + old[i].destroy (old[i].data); + } + + return; + } + } + + g_datalist_unlock (datalist); +} + /** * g_dataset_id_set_data_full: (skip) * @dataset_location: (not nullable): the location identifying the dataset. @@ -672,6 +752,29 @@ g_datalist_id_set_data_full (GData **datalist, g_data_set_internal (datalist, key_id, data, destroy_func, NULL); } +/** + * g_datalist_id_remove_multiple: + * @datalist: a datalist + * @keys: (array length=n_keys): keys to remove + * @n_keys: length of @keys, must be <= 16 + * + * Removes multiple keys from a datalist. + * + * This is more efficient than calling g_datalist_id_remove_data() + * multiple times in a row. + * + * Since: 2.74 + */ +void +g_datalist_id_remove_multiple (GData **datalist, + GQuark *keys, + gsize n_keys) +{ + g_return_if_fail (n_keys <= 16); + + g_data_remove_internal (datalist, keys, n_keys); +} + /** * g_dataset_id_remove_no_notify: (skip) * @dataset_location: (not nullable): the location identifying the dataset. diff --git a/glib/gdataset.h b/glib/gdataset.h index a85179c60..a0d44b09e 100644 --- a/glib/gdataset.h +++ b/glib/gdataset.h @@ -55,6 +55,10 @@ void g_datalist_id_set_data_full (GData **datalist, GQuark key_id, gpointer data, GDestroyNotify destroy_func); +GLIB_AVAILABLE_IN_2_74 +void g_datalist_id_remove_multiple (GData **datalist, + GQuark *keys, + gsize n_keys); typedef gpointer (*GDuplicateFunc) (gpointer data, gpointer user_data); From fa8c7c0da6cfc655a66ac9a9e1371f1e686f34bf Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 22 May 2022 07:22:16 -0400 Subject: [PATCH 3/4] gobject: Use g_datalist_id_remove_multiple This speeds up object finalization a little. --- gobject/gobject.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/gobject/gobject.c b/gobject/gobject.c index 4c992d361..e710afc29 100644 --- a/gobject/gobject.c +++ b/gobject/gobject.c @@ -1181,10 +1181,14 @@ g_object_do_get_property (GObject *object, static void g_object_real_dispose (GObject *object) { + GQuark keys[3] = { + quark_closure_array, + quark_weak_refs, + quark_weak_locations, + }; + g_signal_handlers_destroy (object); - g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL); - g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL); - g_datalist_id_set_data (&object->qdata, quark_weak_locations, NULL); + g_datalist_id_remove_multiple (&object->qdata, keys, G_N_ELEMENTS (keys)); } #ifdef G_ENABLE_DEBUG @@ -3678,10 +3682,15 @@ g_object_unref (gpointer _object) } /* we are still in the process of taking away the last ref */ - g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL); g_signal_handlers_destroy (object); - g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL); - g_datalist_id_set_data (&object->qdata, quark_weak_locations, NULL); + { + GQuark keys[3] = { + quark_closure_array, + quark_weak_refs, + quark_weak_locations, + }; + g_datalist_id_remove_multiple (&object->qdata, keys, G_N_ELEMENTS (keys)); + } /* decrement the last reference */ old_ref = g_atomic_int_add (&object->ref_count, -1); From b7be0f95a8aadf24472729799bdcfe52e62ae5b9 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 22 May 2022 08:04:51 -0400 Subject: [PATCH 4/4] gobject: Speed up finalization Move the warning about finalize-during-construction to debug-only. --- gobject/gobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gobject/gobject.c b/gobject/gobject.c index e710afc29..4cb6ea206 100644 --- a/gobject/gobject.c +++ b/gobject/gobject.c @@ -1214,13 +1214,13 @@ floating_check (GObject *object) static void g_object_finalize (GObject *object) { +#ifdef G_ENABLE_DEBUG if (object_in_construction (object)) { g_critical ("object %s %p finalized while still in-construction", G_OBJECT_TYPE_NAME (object), object); } -#ifdef G_ENABLE_DEBUG if (floating_check (object)) { g_critical ("A floating object %s %p was finalized. This means that someone\n"