Garbage Collection and performances with Xamarin.iOS and MonoGame
I recently had the task to port a .Net mobile application to iOS, with Xamarin.iOS.
The application is not a game, but is based on the MonoGame game engine because it requires a lot of fluidity and some advanced multi-touch interactions with the user. Performances are therefore critical.
Most of the porting was simple and almost straightforward, but I struggled for a few days trying to meet the performance requirements: when idle, the app would behave correctly and be very fluid, but as soon as I was starting to actually use it and triggered some code, the fps dropped dramatically and most of the app was unresponsive for at least a few seconds…
The application was already working pretty well on Android, so I was really not expecting this.
After checking a lot of things in the code and in the Xamarin documentation without finding anything, I finally solved the problem after trying some last resort thing…
What is wrong?
It turns out the app was creating and freeing a lot of objects to display its content and perform its job. This would normally not be a problem in a managed world with a GC and a classical application. But it was not a classical application, it was a game, and carelessly creating new objects 60 times per second makes for a lot of objects.
I was aware the code base was not very optimized regarding the allocation of new objects for a game engine, but it had not really been a problem with the Android and Windows versions.
Except the garbage collector implemented by Xamarin on iOS is apparently pretty slow and/or stops everything on every threads each time it runs.
Hence the freezes.
The source of the problem is easily found using the appropriate tools. In this case, the Apple Instruments profiler:
Here, everything is behaving as expected. Nothing much is happening in the application and most of the time is spent in the Draw and Update methods.
In that case however, almost half of the time is spent on some kind of lock, owned by the garbage collector!
How do I solve it?
The solution was fortunately very easy.
Xamarin and Mono are always improving, and one of the last updates of Xamarin.iOS includes a new generational garbage collector called SGen.
SGen still might be experimental, but it is apparently already working pretty well, and activating it was all it took to solve all of the performance problems, compared to Android and Windows!
The ported app is actually running even smoother on iOS, because the C# code is translated to Objective C, so it’s running at (almost) native speed!