aboutsummaryrefslogtreecommitdiff
path: root/include/qemu/qtree.h
diff options
context:
space:
mode:
authorEmilio Cota <cota@braap.org>2023-02-05 11:37:57 -0500
committerRichard Henderson <richard.henderson@linaro.org>2023-03-28 15:23:10 -0700
commite3feb2cc224f61149a27f021042f5a4230bb1008 (patch)
tree5ed971aeac72f9ea55ffba03ebb7c3d313bac295 /include/qemu/qtree.h
parentd37158bb2425e7ebffb167d611be01f1e9e6c86f (diff)
downloadqemu-e3feb2cc224f61149a27f021042f5a4230bb1008.zip
qemu-e3feb2cc224f61149a27f021042f5a4230bb1008.tar.gz
qemu-e3feb2cc224f61149a27f021042f5a4230bb1008.tar.bz2
util: import GTree as QTree
The only reason to add this implementation is to control the memory allocator used. Some users (e.g. TCG) cannot work reliably in multi-threaded environments (e.g. forking in user-mode) with GTree's allocator, GSlice. See https://gitlab.com/qemu-project/qemu/-/issues/285 for details. Importing GTree is a temporary workaround until GTree migrates away from GSlice. This implementation is identical to that in glib v2.75.0, except that we don't import recent additions to the API nor deprecated API calls, none of which are used in QEMU. I've imported tests from glib and added a benchmark just to make sure that performance is similar. Note: it cannot be identical because (1) we are not using GSlice, (2) we use different compilation flags (e.g. -fPIC) and (3) we're linking statically. $ cat /proc/cpuinfo| grep 'model name' | head -1 model name : AMD Ryzen 7 PRO 5850U with Radeon Graphics $ echo '0' | sudo tee /sys/devices/system/cpu/cpufreq/boost $ tests/bench/qtree-bench Tree Op 32 1024 4096 131072 1048576 ------------------------------------------------------------------------------------------------ GTree Lookup 83.23 43.08 25.31 19.40 16.22 QTree Lookup 113.42 (1.36x) 53.83 (1.25x) 28.38 (1.12x) 17.64 (0.91x) 13.04 (0.80x) GTree Insert 44.23 29.37 25.83 19.49 17.03 QTree Insert 46.87 (1.06x) 25.62 (0.87x) 24.29 (0.94x) 16.83 (0.86x) 12.97 (0.76x) GTree Remove 53.27 35.15 31.43 24.64 16.70 QTree Remove 57.32 (1.08x) 41.76 (1.19x) 38.37 (1.22x) 29.30 (1.19x) 15.07 (0.90x) GTree RemoveAll 135.44 127.52 126.72 120.11 64.34 QTree RemoveAll 127.15 (0.94x) 110.37 (0.87x) 107.97 (0.85x) 97.13 (0.81x) 55.10 (0.86x) GTree Traverse 277.71 276.09 272.78 246.72 98.47 QTree Traverse 370.33 (1.33x) 411.97 (1.49x) 400.23 (1.47x) 262.82 (1.07x) 78.52 (0.80x) ------------------------------------------------------------------------------------------------ As a sanity check, the same benchmark when Glib's version is >= $glib_dropped_gslice_version (i.e. QTree == GTree): Tree Op 32 1024 4096 131072 1048576 ------------------------------------------------------------------------------------------------ GTree Lookup 82.72 43.09 24.18 19.73 16.09 QTree Lookup 81.82 (0.99x) 43.10 (1.00x) 24.20 (1.00x) 19.76 (1.00x) 16.26 (1.01x) GTree Insert 45.07 29.62 26.34 19.90 17.18 QTree Insert 45.72 (1.01x) 29.60 (1.00x) 26.38 (1.00x) 19.71 (0.99x) 17.20 (1.00x) GTree Remove 54.48 35.36 31.77 24.97 16.95 QTree Remove 54.46 (1.00x) 35.32 (1.00x) 31.77 (1.00x) 24.91 (1.00x) 17.15 (1.01x) GTree RemoveAll 140.68 127.36 125.43 121.45 68.20 QTree RemoveAll 140.65 (1.00x) 127.64 (1.00x) 125.01 (1.00x) 121.73 (1.00x) 67.06 (0.98x) GTree Traverse 278.68 276.05 266.75 251.65 104.93 QTree Traverse 278.31 (1.00x) 275.78 (1.00x) 266.42 (1.00x) 247.89 (0.99x) 104.58 (1.00x) ------------------------------------------------------------------------------------------------ Signed-off-by: Emilio Cota <cota@braap.org> Message-Id: <20230205163758.416992-2-cota@braap.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'include/qemu/qtree.h')
-rw-r--r--include/qemu/qtree.h201
1 files changed, 201 insertions, 0 deletions
diff --git a/include/qemu/qtree.h b/include/qemu/qtree.h
new file mode 100644
index 0000000..69fe74b
--- /dev/null
+++ b/include/qemu/qtree.h
@@ -0,0 +1,201 @@
+/*
+ * GLIB - Library of useful routines for C programming
+ * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Modified by the GLib Team and others 1997-2000. See the AUTHORS
+ * file for a list of people on the GLib Team. See the ChangeLog
+ * files for a list of changes. These files are distributed with
+ * GLib at ftp://ftp.gtk.org/pub/gtk/.
+ */
+
+/*
+ * QTree is a partial import of Glib's GTree. The parts excluded correspond
+ * to API calls either deprecated (e.g. g_tree_traverse) or recently added
+ * (e.g. g_tree_search_node, added in 2.68); neither have callers in QEMU.
+ *
+ * The reason for this import is to allow us to control the memory allocator
+ * used by the tree implementation. Until Glib 2.75.3, GTree uses Glib's
+ * slice allocator, which causes problems when forking in user-mode;
+ * see https://gitlab.com/qemu-project/qemu/-/issues/285 and glib's
+ * "45b5a6c1e gslice: Remove slice allocator and use malloc() instead".
+ *
+ * TODO: remove QTree when QEMU's minimum Glib version is >= 2.75.3.
+ */
+
+#ifndef QEMU_QTREE_H
+#define QEMU_QTREE_H
+
+#include "qemu/osdep.h"
+
+#ifdef HAVE_GLIB_WITH_SLICE_ALLOCATOR
+
+typedef struct _QTree QTree;
+
+typedef struct _QTreeNode QTreeNode;
+
+typedef gboolean (*QTraverseNodeFunc)(QTreeNode *node,
+ gpointer user_data);
+
+/*
+ * Balanced binary trees
+ */
+QTree *q_tree_new(GCompareFunc key_compare_func);
+QTree *q_tree_new_with_data(GCompareDataFunc key_compare_func,
+ gpointer key_compare_data);
+QTree *q_tree_new_full(GCompareDataFunc key_compare_func,
+ gpointer key_compare_data,
+ GDestroyNotify key_destroy_func,
+ GDestroyNotify value_destroy_func);
+QTree *q_tree_ref(QTree *tree);
+void q_tree_unref(QTree *tree);
+void q_tree_destroy(QTree *tree);
+void q_tree_insert(QTree *tree,
+ gpointer key,
+ gpointer value);
+void q_tree_replace(QTree *tree,
+ gpointer key,
+ gpointer value);
+gboolean q_tree_remove(QTree *tree,
+ gconstpointer key);
+gboolean q_tree_steal(QTree *tree,
+ gconstpointer key);
+gpointer q_tree_lookup(QTree *tree,
+ gconstpointer key);
+gboolean q_tree_lookup_extended(QTree *tree,
+ gconstpointer lookup_key,
+ gpointer *orig_key,
+ gpointer *value);
+void q_tree_foreach(QTree *tree,
+ GTraverseFunc func,
+ gpointer user_data);
+gpointer q_tree_search(QTree *tree,
+ GCompareFunc search_func,
+ gconstpointer user_data);
+gint q_tree_height(QTree *tree);
+gint q_tree_nnodes(QTree *tree);
+
+#else /* !HAVE_GLIB_WITH_SLICE_ALLOCATOR */
+
+typedef GTree QTree;
+typedef GTreeNode QTreeNode;
+typedef GTraverseNodeFunc QTraverseNodeFunc;
+
+static inline QTree *q_tree_new(GCompareFunc key_compare_func)
+{
+ return g_tree_new(key_compare_func);
+}
+
+static inline QTree *q_tree_new_with_data(GCompareDataFunc key_compare_func,
+ gpointer key_compare_data)
+{
+ return g_tree_new_with_data(key_compare_func, key_compare_data);
+}
+
+static inline QTree *q_tree_new_full(GCompareDataFunc key_compare_func,
+ gpointer key_compare_data,
+ GDestroyNotify key_destroy_func,
+ GDestroyNotify value_destroy_func)
+{
+ return g_tree_new_full(key_compare_func, key_compare_data,
+ key_destroy_func, value_destroy_func);
+}
+
+static inline QTree *q_tree_ref(QTree *tree)
+{
+ return g_tree_ref(tree);
+}
+
+static inline void q_tree_unref(QTree *tree)
+{
+ g_tree_unref(tree);
+}
+
+static inline void q_tree_destroy(QTree *tree)
+{
+ g_tree_destroy(tree);
+}
+
+static inline void q_tree_insert(QTree *tree,
+ gpointer key,
+ gpointer value)
+{
+ g_tree_insert(tree, key, value);
+}
+
+static inline void q_tree_replace(QTree *tree,
+ gpointer key,
+ gpointer value)
+{
+ g_tree_replace(tree, key, value);
+}
+
+static inline gboolean q_tree_remove(QTree *tree,
+ gconstpointer key)
+{
+ return g_tree_remove(tree, key);
+}
+
+static inline gboolean q_tree_steal(QTree *tree,
+ gconstpointer key)
+{
+ return g_tree_steal(tree, key);
+}
+
+static inline gpointer q_tree_lookup(QTree *tree,
+ gconstpointer key)
+{
+ return g_tree_lookup(tree, key);
+}
+
+static inline gboolean q_tree_lookup_extended(QTree *tree,
+ gconstpointer lookup_key,
+ gpointer *orig_key,
+ gpointer *value)
+{
+ return g_tree_lookup_extended(tree, lookup_key, orig_key, value);
+}
+
+static inline void q_tree_foreach(QTree *tree,
+ GTraverseFunc func,
+ gpointer user_data)
+{
+ return g_tree_foreach(tree, func, user_data);
+}
+
+static inline gpointer q_tree_search(QTree *tree,
+ GCompareFunc search_func,
+ gconstpointer user_data)
+{
+ return g_tree_search(tree, search_func, user_data);
+}
+
+static inline gint q_tree_height(QTree *tree)
+{
+ return g_tree_height(tree);
+}
+
+static inline gint q_tree_nnodes(QTree *tree)
+{
+ return g_tree_nnodes(tree);
+}
+
+#endif /* HAVE_GLIB_WITH_SLICE_ALLOCATOR */
+
+#endif /* QEMU_QTREE_H */