Improving Xamarin.Mac launch times with Partial Static Registrar
When developing an application, minimizing the time between completing a change and testing it is important for efficiency. Some strategies such as modularization of codebases, unit tests, and exploration with tools such as Xamarin Workbooks can help. The Xamarin.Mac team has been researching infrastructure improvements as well.
One “low hanging fruit” was finding and fixing a number of build time bugs causing applications to unnecessarily rebuild. Full builds are increasingly expensive as the application size grows, but are not the most exciting changes to report.
One rather exciting development though, that just landed in master this week (PR), is the addition of “Partial Static Registrar” support in Xamarin.Mac, a feature first pioneered by Xamarin.iOS. It dramatically reduces the launch time of Xamarin.Mac applications built under the “debug” configuration. For example, with a new Xamarin.Mac application:
· Xamarin.Mac (2.10 Stable and 3.0 Cycle 9) — 4 +/- .2 seconds
· Xamarin.Mac Master — .8 +/- .1 seconds
Understanding how we squeezed out an almost a 5x improvement in debug launch will take a bit of background on what the registrar is, what the difference is between static and dynamic, and what this “partial static” version does.
Under the hood of any Xamarin.Mac application lies the Cocoa framework from Apple and the Objective-C runtime. Building a bridge between that native “world” and the managed “world” your application depends upon is the primary responsibility of Xamarin.Mac. Part of this task is handled by the registrar, which is executed inside NSApplication.Init (). This is one reason that any use of Cocoa APIs in Xamarin.Mac requires NSApplication.Init to be called first.
The registrar’s job is to inform the Objective-C runtime of the existence of your C# classes that derive from classes such as NSApplicationDelegate, NSView, NSWindow, and NSObject. This requires a scan of all types in your application to determine what needs registering and what elements on each type to report.
This scan can be done either “dynamically”, at startup of the application with reflection, or “statically”, as a build time step.
· Static registration, enabled by adding “ — registrar:static” to your additional mmp arguments, can drastically reduce launch times, but can slow down builds times significantly (more than double debug build time in our example). In Cycle 10, this will be defaulted on for release configuration builds (PR).
· Dynamic registration delays this work until application launch and skips code generation, but this additional work can create a noticeable pause (at least two seconds) in application launch . This is especially noticeable in debug configuration builds, which both default to dynamic registration and whose reflection is slower.
Partial static registration, first introduced in Xamarin.iOS 8.13, gives us much of the best of both options. By pre-computing the registration information of every element in Xamarin.Mac.dll and shipping this information with Xamarin.Mac in a static library (that we only need to link at build time), we remove most of the reflection time of the dynamic registrar while not impacting build time.
This feature just landed in master, and will ship next year as part of our Cycle 10 release. Feel free to check it out by downloading a build from Jenkins.
If you are interesting in more information on the registrar:
- Ask about it on https://gitter.im/xamarin/xamarin-macios#