Improve Parallelism in MSBuild

Felix Huang

Starting in Visual Studio 2019 16.3 we have been adding features to improve build parallelism. These features are still experimental, so they are off by default. When developing tools for Android, we introduced clang/gcc to the MSBuild platform. Clang/gcc relied on the parallelism model of the build system but MSBuild only parallelizes at the project level. This led to the creation of Multi-ToolTask (MTT) as a MSBuild Task. It forgoes MSBuild batching system and works around the typical single task limitations. This allows tasks to execution in parallel and engage other scheduling features not present in MSBuild. In this release, we leveraging MTT to the some of the existing Vcxproj build tasks, making existing tasks more parallel and in return improving build throughput.

Getting Started

MTT can be “opt-in” by setting the MSBuild property or environment variable UseMultiToolTask to true. Its usage should be transparent during day-to-day-developer workflow and it fully supports incremental builds in the IDE and on the command line, even when toggling MTT on and off. To set properties, you can set them as environment variables or follow the instructions in Customize your build. For the best effect, apply these properties to all projects within a solution.

Why Use MTT?

When enabled, the MTT uses its built-in scheduler, which enables some features to control its throughput. By setting property EnforceProcessCountAcrossBuilds to true, this will limit the max number of process used by MTT across multiple projects and MSBuild instances. This feature should help to combat slowdown and memory bounds brought on by over-subscription. For extra control, use the MultiProcMaxCount or CL_MPCount properties to define the max number of jobs. CL_MPCount property is set by the IDE (Tools > Options > Projects and Solutions > Maximum Concurrent C++ Compilations). By default, MultiProcMaxCount and CL_MPCount value are equal to the number of CPU logical processors.

Lastly, setting the metadata MultiToolTaskDependency on an item will create a dependency on another item in the same MTT instance. For example, in our project system, we build .cpp source files to generate PCH first and then build their consumer. In MTT, it is possible to describe this dependency and the scheduler will handle the order. With the dependency description, it allows .cpp without dependency on the PCH to run without waiting, opening more parallelism opportunities.

Performance gains will vary between sources base. Kevin wrote this blog to use xperf to measure your performence.  In this release, MTT is only coded to parallelize MIDL, CL, Clang, and FXC (hlsl).  If your project is using Custom Build Tools, then enable Parallel Custom Build Tools with a few clicks.  If there are other tools that you think could benefit, send us feedback.

Send Us Feedback

The feature is still experimental, and so we are still looking for ways to improve it. Tell us what your experience was or suggest ways to improve the system. Our focus is correctness, incrementality, and scalability. Leave your comments below or email us