Skip to content

Commit 79ce33d

Browse files
committed
[runtime] Less reflection
Try to remove as much reflection (both managed and Java) from the native startup sequence. Instead of using full reflection for managed classes, this commit implements class and field loading using token IDs. The IDs are gathered at the time when typemaps are generated and then stored in the generated assembly code, so that the native runtime can simply pass them to Mono in order to load the needed class and its members. Additionally, I noticed that the default Java time zone is no longer requested during startup, so I moved the class lookup to a lazy code path, protected with a mutex. The changes make startup (till the app is displayed) around 30ms faster for plain Xamarin.Android running on Pixel 3 XL
1 parent 02ab8f7 commit 79ce33d

15 files changed

Lines changed: 696 additions & 74 deletions

src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,21 +126,28 @@ void Run (DirectoryAssemblyResolver res)
126126
bool haveMonoAndroid = false;
127127
var allTypemapAssemblies = new HashSet<string> (StringComparer.OrdinalIgnoreCase);
128128
var userAssemblies = new Dictionary<string, string> (StringComparer.OrdinalIgnoreCase);
129+
var tokenIdCollection = new NativeRuntimeTokenIdCollection (Log);
129130
foreach (var assembly in ResolvedAssemblies) {
130131
bool value;
132+
string fileName = Path.GetFileName (assembly.ItemSpec);
133+
134+
if (String.Compare ("Java.Interop.dll", fileName, StringComparison.CurrentCultureIgnoreCase) == 0) {
135+
tokenIdCollection.ProcessJavaInterop (assembly.ItemSpec);
136+
}
137+
131138
if (bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) && value) {
132139
Log.LogDebugMessage ($"Skipping Java Stub Generation for {assembly.ItemSpec}");
133140
continue;
134141
}
135142

136143
bool addAssembly = false;
137-
string fileName = Path.GetFileName (assembly.ItemSpec);
138144
if (!hasExportReference && String.Compare ("Mono.Android.Export.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) {
139145
hasExportReference = true;
140146
addAssembly = true;
141147
} else if (!haveMonoAndroid && String.Compare ("Mono.Android.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) {
142148
haveMonoAndroid = true;
143149
addAssembly = true;
150+
tokenIdCollection.ProcessMonoAndroid (assembly.ItemSpec);
144151
} else if (MonoAndroidHelper.FrameworkAssembliesToTreatAsUserAssemblies.Contains (fileName)) {
145152
if (!bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) || !value) {
146153
string name = Path.GetFileNameWithoutExtension (fileName);
@@ -180,7 +187,7 @@ void Run (DirectoryAssemblyResolver res)
180187

181188
// Step 2 - Generate type maps
182189
// Type mappings need to use all the assemblies, always.
183-
WriteTypeMappings (allJavaTypes, cache);
190+
WriteTypeMappings (allJavaTypes, tokenIdCollection, cache);
184191

185192
var javaTypes = new List<TypeDefinition> ();
186193
foreach (TypeDefinition td in allJavaTypes) {
@@ -423,11 +430,12 @@ void SaveResource (string resource, string filename, string destDir, Func<string
423430
Files.CopyIfStringChanged (template, Path.Combine (destDir, filename));
424431
}
425432

426-
void WriteTypeMappings (List<TypeDefinition> types, TypeDefinitionCache cache)
433+
void WriteTypeMappings (List<TypeDefinition> types, NativeRuntimeTokenIdCollection tokenIdCollection, TypeDefinitionCache cache)
427434
{
428435
var tmg = new TypeMapGenerator ((string message) => Log.LogDebugMessage (message), SupportedAbis);
429436
if (!tmg.Generate (Debug, SkipJniAddNativeMethodRegistrationAttributeScan, types, cache, TypemapOutputDirectory, GenerateNativeAssembly, out ApplicationConfigTaskState appConfState))
430437
throw new XamarinAndroidException (4308, Properties.Resources.XA4308);
438+
appConfState.TokenIdCollection = tokenIdCollection;
431439
GeneratedBinaryTypeMaps = tmg.GeneratedBinaryTypeMaps.ToArray ();
432440
BuildEngine4.RegisterTaskObjectAssemblyLocal (ApplicationConfigTaskState.RegisterTaskObjectKey, appConfState, RegisteredTaskObjectLifetime.Build);
433441
}

src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ void AddEnvironment ()
279279
BoundExceptionType = boundExceptionType,
280280
InstantRunEnabled = InstantRunEnabled,
281281
JniAddNativeMethodRegistrationAttributePresent = appConfState != null ? appConfState.JniAddNativeMethodRegistrationAttributePresent : false,
282+
TokenIdCollection = appConfState?.TokenIdCollection ?? new NativeRuntimeTokenIdCollection (Log),
282283
};
283284

284285
using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) {

src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class ApplicationConfigNativeAssemblyGenerator : NativeAssemblyGenerator
2222
public global::Android.Runtime.BoundExceptionType BoundExceptionType { get; set; }
2323
public bool InstantRunEnabled { get; set; }
2424
public bool JniAddNativeMethodRegistrationAttributePresent { get; set; }
25+
public NativeRuntimeTokenIdCollection TokenIdCollection { get; set; }
2526

2627
public PackageNamingPolicy PackageNamingPolicy { get; set; }
2728

@@ -88,6 +89,86 @@ protected override void WriteSymbols (StreamWriter output)
8889
return size;
8990
});
9091

92+
WriteDataSection (output, "managed_token_ids");
93+
WriteSymbol (output, "managed_token_ids", TargetProvider.GetStructureAlignment (true), packed: false, isGlobal: true, alwaysWriteSize: true, structureWriter: () => {
94+
// Order of fields and their types must correspond *exactly* to that in
95+
// src/monodroid/jni/xamarin-app.h ManagedTokenIds structure
96+
97+
WriteCommentLine (output, "android_runtime_jnienv");
98+
uint size = WriteData (output, TokenIdCollection.AndroidRuntimeJnienv);
99+
100+
WriteCommentLine (output, "android_runtime_jnienv_initialize");
101+
size += WriteData (output, TokenIdCollection.AndroidRuntimeJnienvInitialize);
102+
103+
WriteCommentLine (output, "android_runtime_jnienv_registerjninatives");
104+
size += WriteData (output, TokenIdCollection.AndroidRuntimeJnienvRegisterJniNatives);
105+
106+
WriteCommentLine (output, "android_runtime_jnienv_bridgeprocessing");
107+
size += WriteData (output, TokenIdCollection.AndroidRuntimeJnienvBridgeProcessing);
108+
109+
WriteCommentLine (output, "java_lang_object");
110+
size += WriteData (output, TokenIdCollection.JavaLangObject);
111+
112+
WriteCommentLine (output, "java_lang_object_handle");
113+
size += WriteData (output, TokenIdCollection.JavaLangObjectHandle);
114+
115+
WriteCommentLine (output, "java_lang_object_handle_type");
116+
size += WriteData (output, TokenIdCollection.JavaLangObjectHandleType);
117+
118+
WriteCommentLine (output, "java_lang_object_refs_added");
119+
size += WriteData (output, TokenIdCollection.JavaLangObjectRefsAdded);
120+
121+
WriteCommentLine (output, "java_lang_object_weak_handle");
122+
size += WriteData (output, TokenIdCollection.JavaLangObjectWeakHandle);
123+
124+
WriteCommentLine (output, "java_lang_throwable");
125+
size += WriteData (output, TokenIdCollection.JavaLangThrowable);
126+
127+
WriteCommentLine (output, "java_lang_throwable_handle");
128+
size += WriteData (output, TokenIdCollection.JavaLangThrowableHandle);
129+
130+
WriteCommentLine (output, "java_lang_throwable_handle_type");
131+
size += WriteData (output, TokenIdCollection.JavaLangThrowableHandleType);
132+
133+
WriteCommentLine (output, "java_lang_throwable_refs_added");
134+
size += WriteData (output, TokenIdCollection.JavaLangThrowableRefsAdded);
135+
136+
WriteCommentLine (output, "java_lang_throwable_weak_handle");
137+
size += WriteData (output, TokenIdCollection.JavaLangThrowableWeakHandle);
138+
139+
WriteCommentLine (output, "java_interop_javaobject");
140+
size += WriteData (output, TokenIdCollection.JavaInteropJavaObject);
141+
142+
WriteCommentLine (output, "java_interop_javaobject_handle");
143+
size += WriteData (output, TokenIdCollection.JavaInteropJavaObjectHandle);
144+
145+
WriteCommentLine (output, "java_interop_javaobject_handle_type");
146+
size += WriteData (output, TokenIdCollection.JavaInteropJavaObjectHandleType);
147+
148+
WriteCommentLine (output, "java_interop_javaobject_refs_added");
149+
size += WriteData (output, TokenIdCollection.JavaInteropJavaObjectRefsAdded);
150+
151+
WriteCommentLine (output, "java_interop_javaobject_weak_handle");
152+
size += WriteData (output, TokenIdCollection.JavaInteropJavaObjectWeakHandle);
153+
154+
WriteCommentLine (output, "java_interop_javaexception");
155+
size += WriteData (output, TokenIdCollection.JavaInteropJavaException);
156+
157+
WriteCommentLine (output, "java_interop_javaexception_handle");
158+
size += WriteData (output, TokenIdCollection.JavaInteropJavaExceptionHandle);
159+
160+
WriteCommentLine (output, "java_interop_javaexception_handle_type");
161+
size += WriteData (output, TokenIdCollection.JavaInteropJavaExceptionHandleType);
162+
163+
WriteCommentLine (output, "java_interop_javaexception_refs_added");
164+
size += WriteData (output, TokenIdCollection.JavaInteropJavaExceptionRefsAdded);
165+
166+
WriteCommentLine (output, "java_interop_javaexception_weak_handle");
167+
size += WriteData (output, TokenIdCollection.JavaInteropJavaExceptionWeakHandle);
168+
169+
return size;
170+
});
171+
91172
stringLabel = GetStringLabel ();
92173
WriteData (output, MonoAOTMode ?? String.Empty, stringLabel);
93174
WriteDataSection (output, "mono_aot_mode_name");

src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigTaskState.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ class ApplicationConfigTaskState
55
public const string RegisterTaskObjectKey = "Xamarin.Android.Tasks.ApplicationConfigTaskState";
66

77
public bool JniAddNativeMethodRegistrationAttributePresent { get; set; } = false;
8+
public NativeRuntimeTokenIdCollection TokenIdCollection { get; set; } = default;
89
}
910
}

0 commit comments

Comments
 (0)