共用方式為


My experience with .NET Native Preview

Hello everyone,

Recently the .NET Native Preview has been announced. I was thrilled to read the following lines:

.NET Native compiles C# to native machine code that performs like C++. You will continue to benefit from the productivity and familiarity of the .NET Framework with the great performance of native code. Popular Windows Store apps start up to 60% faster and use 15-20% less memory when compiled with .NET Native.

Of course, when I heard of this I could not wait to give it a try with my recently published app 499+1px and I’d like to share my experiences with you.

The Setup

Getting started was really straight forward: (For a more detailed step-by-step guide, please see here)

  1. Install .NET Native (you can get it here: https://aka.ms/dotnetnative)
  2. Enable my app’s project for .NET Native
  3. Target it at x64 / ARM
  4. Make sure that the checkbox below is marked

image_thumb1

After activating .NET Native, recompiling the app was considerably slower – which was to be expected. C++ compilations usually take a lot longer than .NET compilations. Surprisingly, it just worked. I expected there to be at least a couple of (minor) errors, but it all went through at the first attempt and I was able to run my app.

Testing on my dev machine, I immediately noticed that the app’s main screen showed up faster. Overall, the app felt like it was more fluid and it responded faster to my input. Obviously, this was just my (probably biased) impression and I wanted to get data to confirm it.

To set the context: My app is largely a picture viewer. You get infinitely scrolling image tiles and can drill down into them, to get more details about a photo. The images and all the information is pulled from a web-service in the background and there isn’t too much logic in the application itself. Most processing power goes into displaying lots and lots of images and providing a smooth scrolling experience. Therefore, it only made sense to make use of the new profiling tools that Visual Studio 2013 provides – e.g. the XAML UI Responsiveness tool.

To get realistic test results, I set up my Surface RT with the Remote Tools for Visual Studio 2013 Update 2 RC. I then proceeded to run the following test, compiling the app both with and without .NET Native.

  1. Launch the app
  2. Wait a bit for images to load
  3. Scroll fast to the right
  4. Tap on an image to see its details
  5. Tap on the author to see his/her details

I am aware that this test is not very academic. Firstly, the content of the app depends on the web service behind, which is constantly updated and changing. The speed at which the images are downloaded depends on a lot of external factors. And secondly, I am not a machine and therefore unable to repeat exactly the same scrolling gesture twice, resulting in different scrolling speed and distance. However, I wanted to give the app some use, and not just look at the performance numbers of the start screen without user input. Here is what the graphs of both runs look like.

Managed:

image_thumb4

.NET Native:

image_thumb8

 

Analyzing the results

Let’s start analyzing this with regard to the impressions I gathered when I first launched the app, after compiling it with .NET Native. Let’s look at the start-up time.

Managed:

image_thumb11

.NET Native:

image_thumb15

We can see that the start-up of the app has roughly the same pattern in both traces. However, we can also immediately spot two big differences:

  • The startup of the managed version is a lot wider, which means it took longer
  • During the same period of time, when the managed version just finished starting up, the .NET Native version already executed something else (It’s all Layout and Xaml Other, so presumably I was already scrolling through the app)

There are also a couple of markers at the top of the graphs, which provide us with useful information. There are a couple of them provided automatically for you – like when a page started to load and when it was finally displayed – but you are also able to provide your own markers from within your code, making it a very easy to identify scenarios. For example:

image_thumb17

Using this marker, I was able to tell when the MainPage had been displayed (viewed) in each case:

  • Managed: after 2.969153s
  • .NET Native: after 2.375207s

The .NET Native version took about 20% less time to display the main page. Not bad, considering that the app is almost not executing any code during the startup.

Using the markers, I was also able to determine how long I stayed on the main page. I zoomed into that range and had a look at the listings below the graphs.

Managed:

image_thumb32

.NET Native:

image_thumb34

What we are seeing here is the cost of every element that participated in the layout during the selected period, ordered by duration. Surprisingly, although there were 3 less GridViewItems (3 less photos displayed), the layout of all GridViewItems took 0.25s longer in the .NET Native version. However, it made more than up for that with the GridView: its layout cost was 492.49ms less compared to the managed version.

As a final comparison, I summed up all the items in the list to get the total cost of layout for each version:

  • Managed: 2960.557ms
  • .NET Native: 2698.345ms

Taking into account the different number of GridViewItems, there is still a notable improvement here. Considering how easy it was to set up, I got these performance improvements virtually for free.

 

I am looking forward to test .NET Native with more of my apps. In my next post I am going to have a closer look at the memory improvements. Have you given .NET Native a try already? If yes, I’m looking forward to read your story.

Cheers,

Helge Mahrt

Comments

  • Anonymous
    May 22, 2014
    I understand gains in user code performance, but I am surprised that the layout cost was significantly decreased. Isn't the layout logic implemented in the WinRT framework in C++ anyways?

  • Anonymous
    May 26, 2014
    Layout usually uses involves evaluating DataBindings to your C# classes, which cause transitions from native to managed code and back. According to previous blog posts, .Net Native applies some optimizations which improve this kind of calls. I assume this causes the nice acceleration of the layout pass.

  • Anonymous
    August 24, 2014
    "the layout of all GridViewItems took 0.25s longer in the .NET Native version. However, it made more than up for that with the GridView: its layout cost was 492.49ms less compared to the managed version." Can you get a stack trace (from a debugger - in release mode). I would postulate that the compiler identified an inlining candidate in the GridView.DoStuff() -> GridViewItem.DoStuff() which was a big win, but which makes the resulting non inlined calls a we bit more expensive but with overall big savings. Something .Net native could (an I think should) do is devirtualize (and then possibly inline) virtual calls where it can identify that, with all the compilation units to hand, only one such code path exists. If the resulting inlined function is in fact smaller/the same size than the method invocation preamble (think about some virtual properties implementations and the like) then that could be a very big win.

  • Anonymous
    December 21, 2015
    My experience is massive build times (fro seconds to several minutes), enormous build and object directories (from 50mb to 550mb), slower on some devices.  Not for me I am afraid!