Startup performance matters, as it’s the first feature users come across when using your application. In this post, we’ll walk through some recent samples that improve Xamarin.Mac startup times, especially for large applications, featuring a few build options that change startup time drastically, including one that cuts startup time by 77ms or about 15%.

Let’s start with the SourceWriter, a simple but full Xamarin.Mac application sample that provides a text editor with code completion and syntax highlighting.

Launching a Debug build takes around a half a second, which isn’t bad, but is difficult to measure manually. By applying the technique from this sample and averaging five launches of the application, we can measure an average startup time of 574ms.

To determine how much overhead is related to Debug, switching to Release configuration can drop the time to 454ms, a 20% + improvement! A vast majority of this improvement is due to the Static Registrar, which you can see by adding registrar:static to the MMP arguments (Project Options → Mac Build), giving 477ms.

Registrar Settings

To understand what the registrar settings are doing, we have to dig a bit under the hood. During startup, the Objective-C runtime needs to be informed of the various managed classes that derive from NSObject and the selectors they support. The three options for the registrar setting are Partial Static, Static, and Dynamic.

  • The Partial Static registrar uses reflection on user assemblies during startup and is the default for Debug builds.
  • The Static registrar scans all assemblies and generates code at build time, which is a significant performance improvement (17% in this example). In contrast, it also increases build time, which is why it’s only on by default for Release configurations.
  • There’s also a Dynamic registrar which reflects overall assemblies at startup, not just users assemblies. Previous to Xamarin.Mac 3.2, when the “Partial Static” registrar was introduced, this was the default for Debug configurations.

Compilation

Another job done at startup is a compilation of managed code from IL to machine code. While JIT compilation is incredibly fast in most cases, for large applications it can take up a significant percentage of startup time. Ahead of Time (AOT) compilation compiles assemblies to machine code (dylibs) during the build and bundles them inside the final application bundle.

Enabling AOT requires passing a command line argument via the “Additional MMP arguments” field in Mac Build. Full AOT options will be added to the Mac Build panel in Xamarin.Mac 4.2. The current options are as follows:

In the SourceWriter example, aot:all reduces startup time to a quick 377ms. This is below the 400-500 ms goal that many users will consider “instant,” and a good place to be.

While AOT compilation is great and can provide that last push to get under the desired performance window, it does have some drawbacks to consider. In SourceWriter, it increases build time by 40% and doubles the final application bundle size (i.e. 12 megs to 24 megs).

More Build Settings

Other build settings to consider are Target Framework and Linking mode. Linking can dramatically reduce application size by removing unused code, but is only available in some Target Framework configurations. Applications optimized with Linking are great candidates for AOT compilation.

Even after configuring build settings, your application still might miss your desired performance benchmarks. Consider reviewing the performance documentation and profile your application.

If you’re interested in Xamarin.Mac, Hello, Mac is a great place to get started. There are a number of tutorials covering macOS specific user interface controls and a wealth of samples to look at. The Xamarin.Mac forum community is also open for discussions and questions as well.