MSBuild performance tips

A while back, I posted about a Powershell script I wrote to help analyze build performance. Today I spent a good amount of time looking at the output and tweaking things. I came across a couple BIG performance enhancements that I thought I would share.

Tip #1 – Don’t start external processes

Starting another process is a time consuming activity, and if you are doing it on a post/pre build event, that is going to slow your build down tremendously. I was looking through my project, and found this post build event.

<PropertyGroup>
  <PostBuildEvent>"$(MSBuildBinPath)\msbuild.exe" "$(SolutionDir)ApplyConfiguration.msbuild" /p:Configuration=$(ConfigurationName)</PostBuildEvent>
</PropertyGroup>

In the diagnostic output, I saw the Exec call took 649ms.

Improving performance is simple, just import the MSBuild file and call the target directly. The refactored build looks like the following.

  <Import Project="$(SolutionDir)ApplyConfiguration.msbuild" />
  <Target Name="AfterBuild" DependsOnTargets="InstallConfiguration" />

And the MSBuild timing is pretty amazing, 5ms. That’s half a second in savings.

Tip #2 – Understand Inputs/Outputs

In MSBuild, targets can have a list of input and output files. Before building the target, it checks to make sure there is a need to build based on the timestamps of the inputs and outputs, also known as building incrementally.

Lets look at an example target for compressing Javascript.

<Target Name="CompressJavascript" 
        DependsOnTargets="_CreateScriptFolder;MergeJavascript">
  <Exec Command="..\Bin\jsmin.exe &lt; $(MergedJavascript) &gt; $(CompressedJavascript)" />
</Target>

This executes in about 104 ms. Not a long time, but if you are doing it over and over, it could end up adding up quickly. Refatoring this to execute quickly is extremely simple.

<Target Name="CompressJavascript" 
        Inputs="$(MergedJavascript)"
        Outputs="$(CompressedJavascript)" 
        DependsOnTargets="_CreateScriptFolder;MergeJavascript">
  <Exec Command="..\Bin\jsmin.exe &lt; $(MergedJavascript) &gt; $(CompressedJavascript)" />
</Target>

The first build, it takes the same time. But all subsequent builds execute in 0ms if the $(MergedJavascript) file hasn’t changed.

Tip #3 – Reduce dependencies

After you have optimized everything else, you will see a couple targets that take the majority of your build time, ResolveAssemblyReferences and ResolveProjectReferences. Unfortunately, there isn’t a quick fix for this one. It comes back to your application architecture and dependency management.

Even compiling isn’t that big a deal, since compiling is done incrementally if files have changed. Dependencies have to be resolved each build though.

       78 ms  CoreCompile                                1 calls
      149 ms  ResolveProjectReferences                   1 calls
      188 ms  ResolveAssemblyReferences                  1 calls

The truth about college

Did You Know? from Amybeth on Vimeo.

After watching this video, I was reminded of something that I continually realize in the technology industry (and probably nearly ever industry). You learn very little in college compared to how much you learn on the job. The important thing about college is providing a foundation to get your foot in the door, and teaching you how to learn.

Never stop learning, or you will get left behind.

Are you a software developer that loves to learn? Apply for Rackspace now.