Startup Performance Tuning
By design MugenMvvm is scanning all assemblies in order to match viewmodel->view and caching all needed types. Also by design MugenMvvm bindings works through reflection.
Performance of application startup as far as binding perfromace can be significantly improved by using IBootstrapCodeBuilder interface:
- remove assembly scanning in release builds;
- MugenMvvm bindings will work though compiled expressions instead of reflection access;
- platform linker can be applied (Android, iOS).
Let's look at algorithm of usage IBootstrapCodeBuilder
interface. In order to make this mechanism work you have to follow the next steps:
- Implement
IBootstrapCodeBuilder
interface. - Register the implementation in module. This will generate piece of code that allows all of features be applied.
- Override the module loading procedure in order to use generated code instead of default behaviour.
- Pass generated code into your
MugenMvvmApplicaiton
instance.
Let's look at example of usage IBootstrapCodeBuilder
interface. As our application we will take our HelloValidation app. Clone it on your computer and open the solution.
Let's implement IBootstrapCodeBuilder
interface:
using System;
using System.Collections.Generic;
using System.IO;
using MugenMvvmToolkit.Infrastructure;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.Models;
namespace HelloValidation
{
public class BootstrapCodeBuilder : IBootstrapCodeBuilder
{
private static Dictionary<string, List<string>> _staticTagToCode;
private static Dictionary<string, List<string>> _tagToCode;
void IBootstrapCodeBuilder.AppendStatic(string tag, string code)
{
lock (this)
{
if (_staticTagToCode == null)
_staticTagToCode = new Dictionary<string, List<string>>();
List<string> value;
if (!_staticTagToCode.TryGetValue(tag, out value))
{
value = new List<string>();
_staticTagToCode[tag] = value;
}
value.Add(code);
DumpCode();
}
}
void IBootstrapCodeBuilder.Append(string tag, string code)
{
lock (this)
{
if (_tagToCode == null)
_tagToCode = new Dictionary<string, List<string>>();
List<string> value;
if (!_tagToCode.TryGetValue(tag, out value))
{
value = new List<string>();
_tagToCode[tag] = value;
}
value.Add(code);
DumpCode();
}
}
private static void DumpCode()
{
var list = new List<string>();
var mainMethod = new List<string>();
mainMethod.Add($"public static void PrecompiledBoot({typeof(IModuleContext).FullName} context)");
mainMethod.Add("{");
if (_staticTagToCode != null)
foreach (var builder in _staticTagToCode)
{
var methodName = builder.Key + "StaticGenerated()";
mainMethod.Add(methodName + ";");
list.Add(Environment.NewLine);
list.Add("private static void " + methodName);
list.Add("{");
if (builder.Key == nameof(ExpressionReflectionManager))
builder.Value.Sort();
list.AddRange(builder.Value);
list.Add("}");
}
if (_tagToCode != null)
foreach (var builder in _tagToCode)
{
mainMethod.Add(builder.Key + "Generated(context);");
list.Add(Environment.NewLine);
list.Add("private static void " + builder.Key
+ $"Generated({typeof(IModuleContext).FullName} context)");
list.Add("{");
if (builder.Key == nameof(ExpressionReflectionManager))
builder.Value.Sort();
list.AddRange(builder.Value);
list.Add("}");
}
mainMethod.Add("}");
list.AddRange(mainMethod);
var path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var filename = Path.Combine(path, "BootstrapCodeBuilder.cs");
File.WriteAllLines(filename, list);
}
}
}
and add it in "HelloValidation" android project:
Now we have to register this class as BootstrapCodeBuilder. Let's add new module in android project and register our BootstrapCodeBuilder class:
using MugenMvvmToolkit;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.Modules;
namespace HelloValidation
{
public class Module : ModuleBase
{
#region Constructors
public Module()
: base(false, LoadMode.All, InitializationModulePriority)
{
}
#endregion
#region Overrides of ModuleBase
protected override bool LoadInternal()
{
#if DEBUG
ServiceProvider.BootstrapCodeBuilder = new BootstrapCodeBuilder();
#endif
return true;
}
protected override void UnloadInternal()
{
}
#endregion
}
}
BootstrapCodeBuilder and Release Builds
Note that we used DEBUG macro to register BootstrapCodeBuilder only in debug builds. By design BootstrapCodeBuilder should be called only once before application release (e.g. in Play Store, App Store) and definitely not intended for use in release builds.
Now run your android app. As was written in DumpCode
method of BootstrapCodeBuilder
class generated class BootstrapCodeBuilder.cs
will be saved in personal dir with name "BootstrapCodeBuilder.cs".
var path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var filename = Path.Combine(path, "BootstrapCodeBuilder.cs");
File.WriteAllLines(filename, list);
In our case the path is "/data/data/HelloValidation.HelloValidation/files/BootstrapCodeBuilder.cs".
Let's copy this file from android device on pc using adb and then include generated code into project:
adb pull /data/data/HelloValidation.HelloValidation/files/BootstrapCodeBuilder.cs %USERPROFILE%\Desktop\
Let's add "GeneratedBootstrapCode.cs" file into android project and include generated code into it:
Now we have to point out MugenMvvm engine that we want it use generated code.
Open "App.cs" in "Core" project and modify "App.cs" as follows:
using System;
using System.Collections.Generic;
using System.Reflection;
using Core.ViewModels;
using MugenMvvmToolkit;
using MugenMvvmToolkit.Interfaces.Models;
namespace Core
{
public class App : MvvmApplication
{
private readonly Action<IModuleContext> _loadModulesDelegate;
public App(Action<IModuleContext> loadModulesDelegate = null)
{
_loadModulesDelegate = loadModulesDelegate;
}
protected override void LoadModules(IList<Assembly> assemblies)
{
if (_loadModulesDelegate == null)
base.LoadModules(assemblies);
else
_loadModulesDelegate(CreateModuleContext(Empty.Array<Assembly>()));
}
#region Overrides of MvvmApplication
public override Type GetStartViewModelType()
{
return typeof(MainViewModel);
}
#endregion
}
}
Here we added new constructor and override LoadModules
method in order to use generated code.
Now, in android project modify Setup.cs
as follows:
public class Setup : AndroidBootstrapperBase
{
#region Overrides of AndroidBootstrapperBase
protected override IIocContainer CreateIocContainer()
{
return new AutofacContainer();
}
protected override IMvvmApplication CreateApplication()
{
return new Core.App(GeneratedBootstrapCode.PrecompiledBoot);
}
#endregion
}
In line 12 we passed GeneratedBootstrapCode.PrecompiledBoot
as delegate in App
constructor.
The tuning of startup performance is done. Now our app will use all features of optimized boot.
Generated Code and Linker
Note that this approach fully supports linker on each platform.
Generated Code and Platforms
Despite the fact we considered only android app, similar mechanism is supported by all other platforms (iOS, UWP, Xamarin.Forms).
Updated almost 8 years ago