From 2f2e08173a4794d2a6a77b15a9aea5ad44150f79 Mon Sep 17 00:00:00 2001 From: Dzmitry Konanka Date: Fri, 9 Jul 2021 13:57:53 +0300 Subject: [PATCH] optimize adding unwind info entries by hashtable --- src/mono/mono/mini/unwind.c | 101 ++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/src/mono/mono/mini/unwind.c b/src/mono/mono/mini/unwind.c index bcf9bd4682efc3..bdcfe78604b7cb 100644 --- a/src/mono/mono/mini/unwind.c +++ b/src/mono/mono/mini/unwind.c @@ -30,14 +30,15 @@ typedef struct { typedef struct { guint32 len; - guint8 info [MONO_ZERO_LEN_ARRAY]; + guint8 *info; } MonoUnwindInfo; static mono_mutex_t unwind_mutex; -static MonoUnwindInfo **cached_info; +static MonoUnwindInfo *cached_info; static int cached_info_next, cached_info_size; static GSList *cached_info_list; +static GHashTable *cached_info_ht; /* Statistics */ static int unwind_info_size; @@ -729,6 +730,34 @@ mono_unwind_init (void) mono_counters_register ("Unwind info size", MONO_COUNTER_JIT | MONO_COUNTER_INT, &unwind_info_size); } +static guint +cached_info_hash(gconstpointer key) +{ + guint i, a; + const guint8 *info = cached_info [GPOINTER_TO_UINT (key)].info; + const guint len = cached_info [GPOINTER_TO_UINT (key)].len; + + for (i = a = 0; i != len; ++i) + a ^= (((guint)info [i]) << (i & 0xf)); + + return a; +} + +static gboolean +cached_info_eq(gconstpointer a, gconstpointer b) +{ + const guint32 lena = cached_info [GPOINTER_TO_UINT (a)].len; + const guint32 lenb = cached_info [GPOINTER_TO_UINT (b)].len; + if (lena == lenb) { + const guint8 *infoa = cached_info [GPOINTER_TO_UINT (a)].info; + const guint8 *infob = cached_info [GPOINTER_TO_UINT (b)].info; + if (memcmp (infoa, infob, lena) == 0) + return TRUE; + } + + return FALSE; +} + /* * mono_cache_unwind_info * @@ -742,42 +771,31 @@ mono_unwind_init (void) guint32 mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len) { - int i; - MonoUnwindInfo *info; - + gpointer orig_key; + guint32 i; unwind_lock (); - if (cached_info == NULL) { - cached_info_size = 16; - cached_info = g_new0 (MonoUnwindInfo*, cached_info_size); - } - - for (i = 0; i < cached_info_next; ++i) { - MonoUnwindInfo *cached = cached_info [i]; - - if (cached->len == unwind_info_len && memcmp (cached->info, unwind_info, unwind_info_len) == 0) { - unwind_unlock (); - return i; - } - } + if (!cached_info_ht) + cached_info_ht = g_hash_table_new (cached_info_hash, cached_info_eq); - info = (MonoUnwindInfo *)g_malloc (sizeof (MonoUnwindInfo) + unwind_info_len); - info->len = unwind_info_len; - memcpy (&info->info, unwind_info, unwind_info_len); - - i = cached_info_next; - if (cached_info_next >= cached_info_size) { - MonoUnwindInfo **new_table; + MonoUnwindInfo *new_table; + int new_cached_info_size = cached_info_size ? cached_info_size * 2 : 16; + + /* ensure no integer overflow */ + g_assert (new_cached_info_size > cached_info_size); /* * Avoid freeing the old table so mono_get_cached_unwind_info () * doesn't need locks/hazard pointers. */ + new_table = g_new0 (MonoUnwindInfo, new_cached_info_size ); - new_table = g_new0 (MonoUnwindInfo*, cached_info_size * 2); + /* include array allocations into statistics of memory totally consumed by unwind info */ + unwind_info_size += sizeof (MonoUnwindInfo) * new_cached_info_size ; - memcpy (new_table, cached_info, cached_info_size * sizeof (MonoUnwindInfo*)); + if (cached_info_size) + memcpy (new_table, cached_info, sizeof (MonoUnwindInfo) * cached_info_size); mono_memory_barrier (); @@ -785,14 +803,32 @@ mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len) cached_info = new_table; - cached_info_size *= 2; + cached_info_size = new_cached_info_size; } - cached_info [cached_info_next ++] = info; + i = cached_info_next; + + /* construct temporary element at array's edge without allocated info copy - it will be used for hashtable lookup */ + cached_info [i].len = unwind_info_len; + cached_info [i].info = unwind_info; - unwind_info_size += sizeof (MonoUnwindInfo) + unwind_info_len; + if (!g_hash_table_lookup_extended (cached_info_ht, GUINT_TO_POINTER (i), &orig_key, NULL) ) { + /* hashtable lookup didnt find match - now need to really add new element with allocated copy of unwind info */ + cached_info [i].info = g_new (guint8, unwind_info_len); + memcpy (cached_info [i].info, unwind_info, unwind_info_len); + + /* include allocated memory in stats, note that hashtable allocates struct of 3 pointers per each entry */ + unwind_info_size += sizeof (void *) * 3 + unwind_info_len; + g_hash_table_insert_replace (cached_info_ht, GUINT_TO_POINTER (i), NULL, TRUE); + + cached_info_next = i + 1; + + } else { + i = GPOINTER_TO_UINT (orig_key); + } unwind_unlock (); + return i; } @@ -802,7 +838,6 @@ mono_cache_unwind_info (guint8 *unwind_info, guint32 unwind_info_len) guint8* mono_get_cached_unwind_info (guint32 index, guint32 *unwind_info_len) { - MonoUnwindInfo **table; MonoUnwindInfo *info; guint8 *data; @@ -810,9 +845,7 @@ mono_get_cached_unwind_info (guint32 index, guint32 *unwind_info_len) * This doesn't need any locks/hazard pointers, * since new tables are copies of the old ones. */ - table = cached_info; - - info = table [index]; + info = &cached_info [index]; *unwind_info_len = info->len; data = info->info;