Skip to content

Commit c865a54

Browse files
authored
[Mono] Optimize mono_dl_build_path for component and Android module loading. (#54971)
* Optimize mono_dl_build_path for component and Android module loading. mono_dl_build_path probes a lot of variations of paths and in some situations where modules uses platform specific naming, it could try at least two paths before getting to the correct one. This have been a problem for Android a long time since IO is slow on devices, so always failing two probes per module load adds to the startup time on Android. For dynamic component loading the existing schema is particular bad since the scenario of disabling a component means that the component won't exist, and using the existing probing it could do 3 or more load attempts before realize that the component is not present and use the stub implmenentation. Commit splits current mono_dl_build_path probing logic so it can be customized. For components a new function has been added that will always use platform prefix and platform suffixes, and if not found it will stop looking. For most platform that means only one attempt to load a component instead of at least 3, optimize both the scenario when the component is awailable as well as when it should be disabled. Commit also change the default behavior of mono_dl_build_path on Android reversing current schema making it more likely to find modules using platform specific naming, like libMyLibrary.so, in first attempt. It will still fallback using no prefix and default suffix and then the verbatim name to make sure dllimports will still work as before, but favorizing modules using platform specific naming. On all other platforms mono_dl_build_path will continue to keep its current behaviour.
1 parent dd63190 commit c865a54

File tree

4 files changed

+188
-74
lines changed

4 files changed

+188
-74
lines changed

src/mono/mono/metadata/components.c

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,23 @@
1616
#include "mono/utils/mono-logger-internals.h"
1717
#include "mono/utils/mono-path.h"
1818
#include "mono/utils/mono-time.h"
19+
#include "mono/utils/refcount.h"
1920

2021
static gint64 event_pipe_100ns_ticks;
2122

2223
typedef MonoComponent * (*MonoComponentInitFn) (void);
2324

25+
typedef struct _MonoComponentLibrary {
26+
MonoRefCount ref;
27+
MonoDl *lib;
28+
} MonoComponentLibrary;
29+
2430
typedef struct _MonoComponentEntry {
2531
const char *lib_name;
2632
const char *name;
2733
MonoComponentInitFn init;
2834
MonoComponent **component;
29-
MonoDl *lib;
35+
MonoComponentLibrary *lib;
3036
} MonoComponentEntry;
3137

3238
#define COMPONENT_INIT_FUNC(name) (MonoComponentInitFn) mono_component_ ## name ## _init
@@ -58,8 +64,10 @@ MonoComponentEntry components[] = {
5864
};
5965

6066
#ifndef STATIC_COMPONENTS
67+
static GHashTable *component_library_load_history = NULL;
68+
6169
static MonoComponent*
62-
get_component (const MonoComponentEntry *component, MonoDl **component_lib);
70+
get_component (const MonoComponentEntry *component, MonoComponentLibrary **component_lib);
6371
#endif
6472

6573
void
@@ -82,12 +90,14 @@ mono_components_init (void)
8290
for (int i = 0; i < G_N_ELEMENTS (components); ++i)
8391
*components [i].component = components [i].init ();
8492
#else
93+
component_library_load_history = g_hash_table_new (g_str_hash, g_str_equal);
94+
8595
/* call get_component for each component and init it or its stubs and add it to loaded_components */
86-
MonoDl *lib = NULL;
96+
MonoComponentLibrary *component_lib = NULL;
8797

8898
for (int i = 0; i < G_N_ELEMENTS (components); ++i) {
89-
*components [i].component = get_component (&components [i], &lib);
90-
components [i].lib = lib;
99+
*components [i].component = get_component (&components [i], &component_lib);
100+
components [i].lib = component_lib;
91101
if (!*components [i].component)
92102
*components [i].component = components [i].init ();
93103
}
@@ -106,11 +116,11 @@ component_init_name (const MonoComponentEntry *component)
106116
}
107117

108118
static gpointer
109-
load_component_entrypoint (MonoDl *lib, const MonoComponentEntry *component)
119+
load_component_entrypoint (MonoComponentLibrary *component_lib, const MonoComponentEntry *component)
110120
{
111121
char *component_init = component_init_name (component);
112122
gpointer sym = NULL;
113-
char *error_msg = mono_dl_symbol (lib, component_init, &sym);
123+
char *error_msg = mono_dl_symbol (component_lib->lib, component_init, &sym);
114124
if (error_msg) {
115125
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s library does not have symbol %s: %s", component->name, component_init, error_msg);
116126
g_free (error_msg);
@@ -150,59 +160,76 @@ try_load (const char* dir, const MonoComponentEntry *component, const char* comp
150160
char *path = NULL;
151161
void *iter = NULL;
152162

153-
while ((path = mono_dl_build_path (dir, component_base_lib, &iter))) {
163+
while (lib == NULL && (path = mono_dl_build_platform_path (dir, component_base_lib, &iter))) {
154164
char *error_msg = NULL;
155165
lib = mono_dl_open (path, MONO_DL_EAGER | MONO_DL_LOCAL, &error_msg);
156-
if (lib)
157-
break;
158166
if (!lib) {
159-
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s not found: %s", component->name, error_msg);
167+
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component library %s not found at %s: %s", component_base_lib, path, error_msg);
168+
g_free (error_msg);
169+
} else {
170+
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component library %s found at %s", component_base_lib, path);
160171
}
161-
g_free (error_msg);
162172
g_free (path);
163173
}
164-
if (lib)
165-
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s found at %s", component->name, path);
166-
g_free (path);
174+
167175
return lib;
168176
}
169177

178+
179+
170180
static MonoComponentInitFn
171-
load_component (const MonoComponentEntry *component, MonoDl **lib_out)
181+
load_component (const MonoComponentEntry *component, MonoComponentLibrary **component_lib_out)
172182
{
173183
// If init method has been static linked not using stub library, use that instead of dynamic component.
174184
if (component->init() && component->init()->available ()) {
175-
*lib_out = NULL;
185+
*component_lib_out = NULL;
176186
return component->init;
177187
}
178188

179189
char *component_base_lib = component_library_base_name (component);
180190
MonoComponentInitFn result = NULL;
191+
MonoComponentLibrary *component_lib = NULL;
181192

182-
/* FIXME: just copy what mono_profiler_load does, assuming it works */
193+
// Check history of component library loads to reduce try_load attempts (optimization for libraries hosting multiple components).
194+
if (!g_hash_table_lookup_extended (component_library_load_history, component_base_lib, NULL, (gpointer *)&component_lib)) {
195+
MonoDl *lib = NULL;
196+
#if !defined(HOST_IOS) && !defined(HOST_TVOS) && !defined(HOST_WATCHOS) && !defined(HOST_MACCAT) && !defined(HOST_ANDROID)
197+
lib = try_load (components_dir (), component, component_base_lib);
198+
#endif
199+
if (!lib)
200+
lib = try_load (NULL, component, component_base_lib);
183201

184-
/* FIXME: do I need to provide a path? */
185-
MonoDl *lib = NULL;
186-
lib = try_load (components_dir (), component, component_base_lib);
187-
if (!lib)
188-
lib = try_load (NULL, component, component_base_lib);
202+
component_lib = g_new0 (MonoComponentLibrary, 1);
203+
if (component_lib) {
204+
mono_refcount_init (component_lib, NULL);
205+
component_lib->lib = lib;
206+
}
189207

190-
g_free (component_base_lib);
191-
if (!lib)
208+
g_hash_table_insert (component_library_load_history, g_strdup (component_base_lib), (gpointer)component_lib);
209+
}
210+
211+
if (!component_lib || !component_lib->lib) {
212+
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s not found", component->name);
192213
goto done;
214+
}
215+
216+
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "Component %s found in %s", component->name, component_base_lib);
193217

194-
gpointer sym = load_component_entrypoint (lib, component);
218+
gpointer sym = load_component_entrypoint (component_lib, component);
195219

196220
result = (MonoComponentInitFn)sym;
197-
*lib_out = lib;
221+
222+
mono_refcount_inc (component_lib);
223+
*component_lib_out = component_lib;
198224
done:
225+
g_free (component_base_lib);
199226
return result;
200227
}
201228

202229
MonoComponent*
203-
get_component (const MonoComponentEntry *component, MonoDl **lib_out)
230+
get_component (const MonoComponentEntry *component, MonoComponentLibrary **component_lib_out)
204231
{
205-
MonoComponentInitFn initfn = load_component (component, lib_out);
232+
MonoComponentInitFn initfn = load_component (component, component_lib_out);
206233
if (!initfn)
207234
return NULL;
208235
return initfn();

src/mono/mono/metadata/native-library.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -493,20 +493,18 @@ static MonoDl *
493493
netcore_probe_for_module_variations (const char *mdirname, const char *file_name, int raw_flags)
494494
{
495495
void *iter = NULL;
496-
char *full_name;
496+
char *full_name = NULL;
497497
MonoDl *module = NULL;
498498

499-
// FIXME: this appears to search *.dylib twice for some reason
500-
while ((full_name = mono_dl_build_path (mdirname, file_name, &iter)) && module == NULL) {
501-
char *error_msg;
499+
while (module == NULL && (full_name = mono_dl_build_path (mdirname, file_name, &iter))) {
500+
char *error_msg = NULL;
502501
module = mono_dl_open_full (full_name, MONO_DL_LAZY, raw_flags, &error_msg);
503502
if (!module) {
504503
mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_DLLIMPORT, "DllImport error loading library '%s': '%s'.", full_name, error_msg);
505504
g_free (error_msg);
506505
}
507506
g_free (full_name);
508507
}
509-
g_free (full_name);
510508

511509
return module;
512510
}

src/mono/mono/utils/mono-dl.c

Lines changed: 128 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -390,35 +390,12 @@ mono_dl_close (MonoDl *module)
390390
g_free (module);
391391
}
392392

393-
/**
394-
* mono_dl_build_path:
395-
* \param directory optional directory
396-
* \param name base name of the library
397-
* \param iter iterator token
398-
* Given a directory name and the base name of a library, iterate
399-
* over the possible file names of the library, taking into account
400-
* the possible different suffixes and prefixes on the host platform.
401-
*
402-
* The returned file name must be freed by the caller.
403-
* \p iter must point to a NULL pointer the first time the function is called
404-
* and then passed unchanged to the following calls.
405-
* \returns the filename or NULL at the end of the iteration
406-
*/
407-
char*
408-
mono_dl_build_path (const char *directory, const char *name, void **iter)
409-
{
410-
int idx;
411-
const char *prefix;
412-
const char *suffix;
413-
gboolean need_prefix = TRUE, need_suffix = TRUE;
414-
int prlen;
415-
int suffixlen;
416-
char *res;
417-
int iteration;
418-
419-
if (!iter)
420-
return NULL;
393+
typedef gboolean (*dl_library_name_formatting_func)(int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix);
421394

395+
static
396+
gboolean
397+
dl_default_library_name_formatting (int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix)
398+
{
422399
/*
423400
The first time we are called, idx = 0 (as *iter is initialized to NULL). This is our
424401
"bootstrap" phase in which we check the passed name verbatim and only if we fail to find
@@ -428,26 +405,79 @@ mono_dl_build_path (const char *directory, const char *name, void **iter)
428405
libsomething.so.1.1 or libsomething.so - testing it algorithmically would be an overkill
429406
here.
430407
*/
431-
iteration = GPOINTER_TO_UINT (*iter);
432-
idx = iteration;
433408
if (idx == 0) {
434409
/* Name */
435-
need_prefix = FALSE;
436-
need_suffix = FALSE;
437-
suffix = "";
410+
*need_prefix = FALSE;
411+
*need_suffix = FALSE;
412+
*suffix = "";
438413
} else if (idx == 1) {
439414
/* netcore system libs have a suffix but no prefix */
440-
need_prefix = FALSE;
441-
need_suffix = TRUE;
442-
suffix = mono_dl_get_so_suffixes () [0];
443-
suffixlen = strlen (suffix);
415+
*need_prefix = FALSE;
416+
*need_suffix = TRUE;
417+
*suffix = mono_dl_get_so_suffixes () [0];
444418
} else {
445419
/* Prefix.Name.suffix */
446-
suffix = mono_dl_get_so_suffixes () [idx - 2];
447-
if (suffix [0] == '\0')
448-
return NULL;
420+
*need_prefix = TRUE;
421+
*need_suffix = TRUE;
422+
*suffix = mono_dl_get_so_suffixes () [idx - 2];
423+
if ((*suffix) [0] == '\0')
424+
return FALSE;
425+
}
426+
427+
return TRUE;
428+
}
429+
430+
static
431+
gboolean
432+
dl_reverse_library_name_formatting (int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix)
433+
{
434+
const char ** suffixes = mono_dl_get_so_suffixes ();
435+
int suffix_count = 0;
436+
437+
while (suffixes [suffix_count][0] != '\0')
438+
suffix_count++;
439+
440+
if (idx < suffix_count) {
441+
/* Prefix.Name.suffix */
442+
*need_prefix = TRUE;
443+
*need_suffix = TRUE;
444+
*suffix = suffixes [idx];
445+
} else if (idx == suffix_count) {
446+
/* netcore system libs have a suffix but no prefix */
447+
*need_prefix = FALSE;
448+
*need_suffix = TRUE;
449+
*suffix = suffixes [0];
450+
} else if (idx == suffix_count + 1) {
451+
/* Name */
452+
*need_prefix = FALSE;
453+
*need_suffix = FALSE;
454+
*suffix = "";
455+
} else {
456+
return FALSE;
449457
}
450458

459+
return TRUE;
460+
}
461+
462+
static char*
463+
dl_build_path (const char *directory, const char *name, void **iter, dl_library_name_formatting_func func)
464+
{
465+
const char *prefix;
466+
const char *suffix;
467+
gboolean need_prefix = TRUE, need_suffix = TRUE;
468+
int prlen;
469+
int suffixlen;
470+
char *res;
471+
int iteration;
472+
473+
if (!iter)
474+
return NULL;
475+
476+
477+
iteration = GPOINTER_TO_UINT (*iter);
478+
if (!func (iteration, &need_prefix, &need_suffix, &suffix))
479+
return NULL;
480+
451481
if (need_prefix) {
452482
prlen = strlen (mono_dl_get_so_prefix ());
453483
if (prlen && strncmp (name, mono_dl_get_so_prefix (), prlen) != 0)
@@ -471,6 +501,64 @@ mono_dl_build_path (const char *directory, const char *name, void **iter)
471501
return res;
472502
}
473503

504+
/**
505+
* mono_dl_build_path:
506+
* \param directory optional directory
507+
* \param name base name of the library
508+
* \param iter iterator token
509+
* Given a directory name and the base name of a library, iterate
510+
* over the possible file names of the library, taking into account
511+
* the possible different suffixes and prefixes on the host platform.
512+
*
513+
* The returned file name must be freed by the caller.
514+
* \p iter must point to a NULL pointer the first time the function is called
515+
* and then passed unchanged to the following calls.
516+
* \returns the filename or NULL at the end of the iteration
517+
*/
518+
char*
519+
mono_dl_build_path (const char *directory, const char *name, void **iter)
520+
{
521+
#ifdef HOST_ANDROID
522+
return dl_build_path (directory, name, iter, dl_reverse_library_name_formatting);
523+
#else
524+
return dl_build_path (directory, name, iter, dl_default_library_name_formatting);
525+
#endif
526+
}
527+
528+
static
529+
gboolean
530+
dl_prefix_suffix_library_name_formatting (int idx, gboolean *need_prefix, gboolean *need_suffix, const char **suffix)
531+
{
532+
/* Prefix.Name.suffix */
533+
*need_prefix = TRUE;
534+
*need_suffix = TRUE;
535+
*suffix = mono_dl_get_so_suffixes () [idx];
536+
if ((*suffix) [0] == '\0')
537+
return FALSE;
538+
539+
return TRUE;
540+
}
541+
542+
/**
543+
* mono_dl_build_platform_path:
544+
* \param directory optional directory
545+
* \param name base name of the library
546+
* \param iter iterator token
547+
* Given a directory name and the base name of a library, iterate
548+
* over platform prefix and suffixes generating a library name
549+
* suitable for the platform. Function won't try additional fallbacks.
550+
*
551+
* The returned file name must be freed by the caller.
552+
* \p iter must point to a NULL pointer the first time the function is called
553+
* and then passed unchanged to the following calls.
554+
* \returns the filename or NULL at the end of the iteration
555+
*/
556+
char*
557+
mono_dl_build_platform_path (const char *directory, const char *name, void **iter)
558+
{
559+
return dl_build_path (directory, name, iter, dl_prefix_suffix_library_name_formatting);
560+
}
561+
474562
MonoDlFallbackHandler *
475563
mono_dl_fallback_register (MonoDlFallbackLoad load_func, MonoDlFallbackSymbol symbol_func, MonoDlFallbackClose close_func, void *user_data)
476564
{

src/mono/mono/utils/mono-dl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ MONO_EXTERN_C
3636
void mono_dl_close (MonoDl *module);
3737

3838
char* mono_dl_build_path (const char *directory, const char *name, void **iter);
39+
char* mono_dl_build_platform_path (const char *directory, const char *name, void **iter);
3940

4041
MonoDl* mono_dl_open_runtime_lib (const char *lib_name, int flags, char **error_msg);
4142

0 commit comments

Comments
 (0)