Speeding up compilation in UWP?

Viewed 3041

I'm working on UWP after working for a long time in WPF and compilation is abysmally slow.

I understand why release compilation is slow (net native compilation) but that's disabled in debug yet it still takes a lot of time between F5 and the application being displayed on screen, even for a blank application.

Is there any way to speed this up (even at the cost of runtime performance)? It's really hard to work with when you're used to C# giving you extremely fast compile times and testing after every small changes.

For exemple just a right click and rebuild on a very simple (4 pages, each < 200 line of xaml, and pretty much 0 C#) uwp project takes almost exactly 20 seconds in debug (without .net native) with no project references. Meanwhile a much larger WPF application (dozens of windows, thouthand of lines of codes) takes a few seconds and most of that time is copying child projects!

As suggested you can find a minimal example for download here : https://www.dropbox.com/s/0h89qsz66erba3x/WPFUWPCompileSpeed.zip?dl=0

It's just a solution with a blank wpf & blank uwp app. Compilation time for WPF is just over 1 second, for UWP 12 seconds, in each case solution was cleaned and the single project i was testing was right clicked and rebuilt. This is in debug (without .net Native compilation)

2 Answers

I definitely agree that UWP compilation is significantly slower than WPF. I've always had to break apart my WPF assemblies whenever I reached about 5-6 dozen xaml windows or views, in order to keep the incremental compilation times down at 10-20 seconds. The way things are looking, my UWP assemblies will probably grow to only about 2 or 3 dozen items before they take way too long to compile. Any assembly that takes over 10 seconds to compile is problematic when you are trying to do iterative coding & debugging.

Here are two recommendations, if you haven't tried them. The first thing to do is go to the build tab and always remember to uncheck "compile with .NET native tool chain". That is pretty useless for normal debug/test iterations.

The next thing to do is to monitor your build operations with procmon. That will surface any issues that may be specific to your workstation (like virus scanning, other apps that may be interfering).

Here are the factors I noticed that would slow down UWP compilation compared to WPF:

  • Lots of compilation passes (see them in the build output window):

> MarkupCompilePass1:
> XamlPreCompile:
> MarkupCompilePass2:
> GenerateTargetFrameworkMonikerAttribute:
> CoreCompile:
> CopyGeneratedXaml:
> CopyFilesToOutputDirectory:
> ComputeProcessXamlFiles:
> CustomOutputGroupForPackaging:
> GetPackagingOutputs:
> _GenerateProjectPriConfigurationFiles:
> _GenerateProjectPriFileCore:
  • Core/Nuget dependencies

Unlike with the .Net Framework, all your dependencies for UWP are coming from .Net core and nuget. You should be able to use procmon and see tons of reads against the directory where VS keeps the stuff: C:\Program Files (x86)\Microsoft SDKs\UWPNuGetPackages. Note that the dependencies themselves aren't of a totally different quality than the .Net framework dependencies (in WPF). But there is a big difference in how those dependencies are gathered and how they are pushed around in your output directories during compile operations (eventually ending up in the final Appx directory).

  • No "shared output directory" for optimization.

With WPF we could send class library targets individually to a shared output directory (by checking them for build, while unchecking other libraries and the application exe itself). Then we could launch the debugger and without compiling a whole ton of stuff that wasn't necessarily changing. However, UWP requires us to build the entry-level application, regardless of whether we try to configure a shared output directory.

  • The new "Deploy" step is required in UWP

WPF didn't have the new "deploy" step that is required every time you build a UWP app. You will see the checkbox in the build configuration, and it applies to the entry-level application. If you don't deploy, you won't see all your changes while debugging.

  • UWP is still actively changing (unlike WPF)

They keep changing the UWP compilation operation. Soon we are going to start to see something called the "WinUI" library that will introduce another dependency from NuGet for UWP apps. This is probably not going to help matters where compilation performance is concerned.

I think once the pace of change starts to slow down, Microsoft may decide to focus on finding ways to improve the performance of compiles. It seems pretty clear when reading the procmon output that less than half of the work done by devenv.exe is especially being done for me. The rest of it is pulling in all the dependencies so that my code can compile against it. There has to be a way to optimize that repetitive processing.

Ronan the (twelve-second?) compile time for an empty project does seem a bit high... Mine generally runs for under five seconds (when empty).

You might want to watch what is going on with CPU. The compilation of a single project should be entirely cpu-bound, especially on the second or third attempt, after the file system has cached your source code (and all the nuget dependencies are downloaded). You may want to keep an eye on task-manager and make sure that you are running at full speed on a single core (ie 25% if you have four cores, 12% for eight cores, etc). If you see CPU drop down too far then something is going wrong. Also make sure to check that CPU is only being used by the usual things that you would expect, eg devenv.exe, VBCSCompiler.exe and MSBuild.exe.

For those who claim to be compiling UWP projects so much faster than everyone else, it might be interesting to hear their benchmarks for the windows community toolkit. (https://github.com/windows-toolkit/WindowsCommunityToolkit/tree/rel/5.0.0) The most interesting compile times would be for projects that are most heavy on xaml. Here are my times:

  • 10 seconds for Microsoft.Toolkit.Uwp.UI.Controls
  • 8 seconds Microsoft.Toolkit.Uwp.UI.Controls.Graph
  • 8 seconds Microsoft.Toolkit.Uwp.UI.Controls.DataGrid

The basic Van Arsdel inventory app is another thing to benchmark. https://github.com/Microsoft/InventorySample Here is my result:

  • 14 seconds for Inventory.App

The VanArsdel sample app would be another good one to benchmark. (Careful with this; you may have to be on a Windows insider build and it may mess up your VS like it did mine). The url is here: https://github.com/microsoft/vanarsdel Here is my result:

  • 13 seconds for VanArsdel project

Remember that projects which are heavily xaml-oriented are also usually the slowest because they involve more processing work during the various compilation passes.

Using Diagnostic Output

While the total elapsed time to build a project is critical, it may also be helpful to understand where the time is coming from. You can get a summary of that if you go to Tools->Options->Build-and-Run and then turn up the project build output verbosity to Diagnostic. Then compile the project in question. At the end you will see a Task Performance Summary that looks like the following. Note that CompileXaml should take the most time. These five items seem to be pretty common.

Task Performance Summary:

  100 ms  ValidateAppxManifest                       1 calls
  400 ms  ExpandPriContent                           1 calls
  400 ms  Csc                                        2 calls
  600 ms  ResolveAssemblyReference                   1 calls
 7000 ms  CompileXaml                                2 calls

If you see anything else (eg GenerateAppxManifest) taking up a few seconds every time you compile then that is probably a corruption problem within your project. You should be able to troubleshoot by searching for the word "completely", as in "Building target _GenerateCurrentProjectAppxManifest completely". When you find that, it should tell you why it is doing the extra work.

Min Version Targeting

I noticed that changing the min version on the targeting tab will cut out three seconds out of my "CompileXaml" time.

See below that changing min version to 15063 helps cut down on compile time. I suspect this is related to bloat in the related dependencies.

15063 (creators update) [4 seconds]
16299 (fall creators) [7 seconds]
17134 (version 1803) [ 7.5 seconds]

Its not always an option to target the creators update, so this may not be general-purpose fix. But it is interesting to know about the impact of getting an updated Windows 10 SDK.

Related