27 November 2011

Speed up your build with UseHardlinksIfPossible

MsBuild 4.0 added the new attribute "UseHardlinksIfPossible" to the Copy task. Using Hardlinks makes your build faster because less IO operations and disk space is needed (= better usage of file system cache). What's best is that this new option is already be used by the standard .net build system! But Microsoft decided to turn them off by default.

After searching a little bit in the c# target files I found out how to turn this feature globally on and my build was 20% faster than before. And if you have big builds with more than a hundred projects this counts!

So here comes the way to turn on hard linking in your build. First, this works only with NTFS. Second you have to explicitly set the ToolsVersion to 4.0. You can do this with a command line argument (msbuild /tv:4.0) or inside the project file (<Project DefaultTargets="Build" ToolsVersion="4.0" ...).

Then you have to override the following properties with a value of "True":

  • CreateHardLinksForCopyFilesToOutputDirectoryIfPossible
  • CreateHardLinksForCopyAdditionalFilesIfPossible
  • CreateHardLinksForCopyLocalIfPossible
  • CreateHardLinksForPublishFilesIfPossible

Use command line properties (msbuild /p:CreateHardLinksForCopyLocalIfPossible=true) to override them for all projects in one build. Or you can create a little startup build file that collects all projects and set the properties in one place. Here is mine:

Code Snippet
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >

  <PropertyGroup>    
    <Include>**\*.csproj</Include>
    <Exclude>_build\**\*.csproj</Exclude>
    <CreateHardLinksIfPossible>true</CreateHardLinksIfPossible>
  </PropertyGroup>

  <ItemGroup>
    <ProjectFiles Include="$(Include)" Exclude="$(Exclude)"/>
  </ItemGroup>

  <Target Name="Build" >
    <MSBuild Projects="@(ProjectFiles)" Targets="Build" BuildInParallel="True" ToolsVersion="4.0"
             Properties="Configuration=$(Configuration);
                         BuildInParallel=True;
                         CreateHardLinksForCopyFilesToOutputDirectoryIfPossible=$(CreateHardLinksIfPossible);
                         CreateHardLinksForCopyAdditionalFilesIfPossible=$(CreateHardLinksIfPossible);
                         CreateHardLinksForCopyLocalIfPossible=$(CreateHardLinksIfPossible);
                         CreateHardLinksForPublishFilesIfPossible=$(CreateHardLinksIfPossible);
                         " />    
  </Target>
  
</Project>

3 comments:

  1. Nice - I'll give that a try soon.

    ReplyDelete
  2. It seems to me that all of this only works when building with MSBuild directly (e.g. invoking msbuild via command line), and not when building within Visual Studio.

    I mean, it seems that Mircosoft deliberately disabled the option to set any of these "CreateHardLinks..." properties to True when building inside Visual Studio:

    ""
    (Microsoft.Common.targets)

    ReplyDelete
    Replies
    1. The empty quotes above should read:
      "By default we're not using Hard Links to copy to the output directory, and never when building in VS"

      Delete