A .NET MAUI performance test similar to:
- https://github.com/maxim-saplin/dopetest_xamarin
- https://github.com/maxim-saplin/dopetest_flutter
- https://github.com/unoplatform/performance/tree/master/src/dopes/DopeTestMaui
This puts a number of labels on the screen with a random color and random rotation -- measuring the "LOLs per second".
In many ways, this test is a bit dubious, but it is worthwhile
comparing three types of apps that use the same underlying Android
stack and Android.Widget.TextView
:
- Xamarin.Forms 5 app - the
Label
class is a cross-platform layer overTextView
, using an older Mono runtime & BCL - .NET MAUI app - the
Label
class is a cross-platform layer overTextView
, using the latest runtime & BCL - .NET 6 Android app - use
TextView
directly, but with the interop overhead of calling from C# into Java - Java Android app - use
TextView
directly, without any C# to Java interop
Using these three apps, I think we have a reasonable estimate of what the overhead of using .NET (and .NET MAUI) comparing against the Java app.
Profiling the .NET MAUI app is also quite useful, I've found several things to fix when doing this:
~179 per second in Xamarin.Forms 5:
Thanks @roubachof for this sample!
~327 per second in .NET 6 MAUI:
Note that I was getting around 397 already in .NET 7 MAUI.
~594 per second in .NET 6:
~682 per second using straight Java:
Each sample has a Thread.Sleep()
such as:
while (count < 5000)
{
// Do stuff
Thread.Sleep(1);
}
I chose a number that the UI thread moved reasonably well visually on
a Pixel 5 device. Some samples I had to put 2, 1, or even an if (count % N == 0)
check.
If the number is too low, you won't see the labels "move" and the UI will appear to freeze/lock up. If it looks smooth the number is probably OK.
If the number is too high, you'll simply get a lower LOLs per second than what is possible.
A reported number is 12,255 per second:
Note that the test may not be exactly the same -- or running on the same device.
The problem with this test in Flutter, is it has a different UI
paradigm than Android.Widget.TextView
. The main bottleneck in the
Java sample is the use of runOnUiThread
, you can't add a TextView
to the screen without this call.
Basically what Flutter is doing:
-
Create a "Label" that is an object that represents text
-
A render thread (running at either 60hz or 120hz) loops over the labels and renders them.
If I were to reproduce this same logic in C#:
-
Create a POCO (plain old C# object) with all the values.
-
Push the objects into a
ConcurrentQueue
-
Call
Invalidate()
-
The UI thread overrides
OnDraw()
, loop over theConcurrentQueue
and draw them.
This results in a completely ridiculous ~152,661 per second:
Thus, I think measuring the LOLs per second with this UI paradigm isn't actually useful or measuring anything. Measuring how many times per second I can create a POCO and place it in a queue? This is effectively what Flutter is doing, and we can also achieve it in C#. Both are basically 60/120 per second depending on how fast the UI thread is refreshing the screen.
However! It is worth considering if something in .NET could be implemented the same way as Flutter. Perhaps measuring battery or CPU usage with 60/120 per second would be a more useful test.
You can find the code for this on the canvas branch.