Skip to content

Annotate Arcade MSBuild tasks with [MSBuildMultiThreadableTask] to avoid TaskHost marshalling in -mt builds #17378

Description

@ViktorHofer

Summary

MSBuild's multi-threaded mode (-mt) runs all build nodes as threads inside a single process. Any task not marked with Microsoft.Build.Framework.MSBuildMultiThreadableTaskAttribute is routed to an out-of-proc sidecar TaskHost for isolation, so every invocation pays cross-process serialization of its inputs and outputs.

No task in this repository is currently annotated, so all ~136 Arcade tasks are marshalled out-of-proc in -mt builds. This measurably eats into the gains from -mt + MSBuild Server + node reuse.

Measured impact

A/B of the runtime inner repo build inside the dotnet/dotnet VMR (win-x64, same source SHA 2eebfbf, same SDK 11.0.100-rc.1.26420.103, MSBuild 18.11.0-1.26420.103). Command lines were identical except:

  • A (baseline): /nr:False, no -mt -> MSBuild Server was requested but not used for this build: node reuse is disabled
  • B: /nr:True -mt -> Reusing the running MSBuild Server node for this build

Wall time went 51.50 min -> 49.64 min, only 1.86 min (3.6%). Meanwhile:

metric A (baseline) B (-mt+server+nodereuse)
TaskHost invocations 849 14,125 (16.6x)
distinct caller processes 8 1 (all nodes are threads)
total node-busy time 13,622 s 12,189 s (-10.5%)
effective parallelism (of 8 nodes) 4.41x 4.09x
sum of non-nesting task time 4,026 s 4,124 s (+98 s)

Engine overhead genuinely dropped (MSBuild task inclusive 126,987s -> 106,276s), but ~100s of that was handed straight back as TaskHost marshalling. Arcade-owned tasks are the bulk of it:

task A B ratio invocations
ChooseBestP2PTargetFrameworkTask 1.5 s 17.6 s 11.8x 1,740
GetLicenseFilePath 0.4 s 15.0 s 36x 1,542
ChooseBestTargetFrameworksTask 0.4 s 12.6 s 32x 1,195
GeneratePackageReport 1.7 s 13.6 s 8.0x 11
ValidateLicense 0.4 s 10.8 s 30x 1,542
GetLastStablePackage 0.1 s 6.3 s 61x 130
GenerateResxSource 1.6 s 6.2 s 3.8x 606
GetMinimumNETStandard 0.3 s 3.0 s 10.3x 60
ApplyPreReleaseSuffix 0.03 s 2.5 s 77x 109
TranslateSource 1.8 s 3.1 s 1.7x 208

That is ~82 s of pure added overhead from 10 Arcade tasks in a single repo build, and it repeats for every repo in the VMR.

Proposed change

Audit each task and add [MSBuildMultiThreadableTask] where it is genuinely thread-safe.

Per MSBuildMultiThreadableTaskAttribute, a task qualifies only if it:

  • does not modify global process state (env vars, current working directory, etc.), and
  • does not depend on global process state, including relative path resolution.

Most of the P1 tasks above are pure item/string transforms, so they should qualify easily.

Implementation note

Arcade currently pins the MSBuild packages at 17.12.50 (eng/Version.Details.props, flowed from dotnet-msbuild), which predates the attribute. Step 1 is simply to flow up the MSBuild package versions to 18.x so MSBuildMultiThreadableTaskAttribute is available from Microsoft.Build.Framework directly:

<MicrosoftBuildPackageVersion>17.12.50</MicrosoftBuildPackageVersion>
<MicrosoftBuildFrameworkPackageVersion>17.12.50</MicrosoftBuildFrameworkPackageVersion>
<MicrosoftBuildTasksCorePackageVersion>17.12.50</MicrosoftBuildTasksCorePackageVersion>
<MicrosoftBuildUtilitiesCorePackageVersion>17.12.50</MicrosoftBuildUtilitiesCorePackageVersion>

The attribute is non-inheritable (Inherited = false), so it must be applied to each concrete task class - not to a shared base such as BuildTask / MSBuildTaskBase / XlfTask.

Task inventory

All concrete MSBuild task classes under src/ (excluding eng/common, abstract bases and test-only stubs). 136 tasks.

P1 - measured hot path (do these first)

  • ChooseBestP2PTargetFrameworkTask - src\Microsoft.DotNet.Build.Tasks.TargetFramework\src\ChooseBestP2PTargetFrameworkTask.cs
  • GetLicenseFilePath - src\Microsoft.DotNet.Arcade.Sdk\src\GetLicenseFilePath.cs
  • ChooseBestTargetFrameworksTask - src\Microsoft.DotNet.Build.Tasks.TargetFramework\src\ChooseBestTargetFrameworksTask.cs
  • GeneratePackageReport - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GeneratePackageReport.cs
  • ValidateLicense - src\Microsoft.DotNet.Arcade.Sdk\src\ValidateLicense.cs
  • GetLastStablePackage - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetLastStablePackage.cs
  • GenerateResxSource - src\Microsoft.DotNet.Arcade.Sdk\src\GenerateResxSource.cs
  • GetMinimumNETStandard - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetMinimumNETStandard.cs
  • ApplyPreReleaseSuffix - src\Microsoft.DotNet.Build.Tasks.Packaging\src\ApplyPreReleaseSuffix.cs
  • TranslateSource - src\Microsoft.DotNet.XliffTasks\Tasks\TranslateSource.cs

P2 / P3 - remaining tasks by package

Microsoft.DotNet.Arcade.Sdk (15)
  • CalculateAssemblyAndFileVersions - src\Microsoft.DotNet.Arcade.Sdk\src\CalculateAssemblyAndFileVersions.cs
  • CheckRequiredDotNetVersion - src\Microsoft.DotNet.Arcade.Sdk\src\CheckRequiredDotNetVersion.cs
  • CompareVersions - src\Microsoft.DotNet.Arcade.Sdk\src\CompareVersions.cs
  • DownloadFile - src\Microsoft.DotNet.Arcade.Sdk\src\DownloadFile.cs
  • ExtractNgenMethodList - src\Microsoft.DotNet.Arcade.Sdk\src\ExtractNgenMethodList.cs
  • GenerateChecksums - src\Microsoft.DotNet.Arcade.Sdk\src\GenerateChecksums.cs
  • GenerateSourcePackageSourceLinkTargetsFile - src\Microsoft.DotNet.Arcade.Sdk\src\GenerateSourcePackageSourceLinkTargetsFile.cs
  • GetAssemblyFullName - src\Microsoft.DotNet.Arcade.Sdk\src\GetAssemblyFullName.cs
  • GroupItemsBy - src\Microsoft.DotNet.Arcade.Sdk\src\GroupItemsBy.cs
  • InstallDotNetCore - src\Microsoft.DotNet.Arcade.Sdk\src\InstallDotNetCore.cs
  • LocateDotNet - src\Microsoft.DotNet.Arcade.Sdk\src\LocateDotNet.cs
  • SaveItems - src\Microsoft.DotNet.Arcade.Sdk\src\SaveItems.cs
  • SetCorFlags - src\Microsoft.DotNet.Arcade.Sdk\src\SetCorFlags.cs
  • SingleError - src\Microsoft.DotNet.Arcade.Sdk\src\SingleError.cs
  • Unsign - src\Microsoft.DotNet.Arcade.Sdk\src\Unsign.cs
Microsoft.DotNet.Build.Tasks.Feed (12)
  • ConfigureInputFeed - src\Microsoft.DotNet.Build.Tasks.Feed\src\ConfigureInputFeed.cs
  • CreateAzureContainerIfNotExists - src\Microsoft.DotNet.Build.Tasks.Feed\src\common\CreateAzureContainerIfNotExists.cs
  • CreateAzureDevOpsFeed - src\Microsoft.DotNet.Build.Tasks.Feed\src\CreateAzureDevOpsFeed.cs
  • CreateNewAzureContainer - src\Microsoft.DotNet.Build.Tasks.Feed\src\common\CreateNewAzureContainer.cs
  • LaunchDebugger - src\Microsoft.DotNet.Build.Tasks.Feed\src\LaunchDebugger.cs
  • PublishArtifactsInManifest - src\Microsoft.DotNet.Build.Tasks.Feed\src\PublishArtifactsInManifest.cs
  • PublishArtifactsInManifestV3 - src\Microsoft.DotNet.Build.Tasks.Feed\src\PublishArtifactsInManifestV3.cs
  • PublishArtifactsInManifestV4 - src\Microsoft.DotNet.Build.Tasks.Feed\src\PublishArtifactsInManifestV4.cs
  • PublishBuildToMaestro - src\Microsoft.DotNet.Build.Tasks.Feed\src\PublishBuildToMaestro.cs
  • PublishSignedAssets - src\Microsoft.DotNet.Build.Tasks.Feed\src\PublishSignedAssets.cs
  • PushToBuildStorage - src\Microsoft.DotNet.Build.Tasks.Feed\src\PushToBuildStorage.cs
  • UploadToAzure - src\Microsoft.DotNet.Build.Tasks.Feed\src\common\UploadToAzure.cs
Microsoft.DotNet.Build.Tasks.FileCatalog (1)
  • GenerateFileCatalog - src\Microsoft.DotNet.Build.Tasks.FileCatalog\GenerateFileCatalog.cs
Microsoft.DotNet.Build.Tasks.Installers (13)
  • CreateChangelogFile - src\Microsoft.DotNet.Build.Tasks.Installers\src\CreateChangelogFile.cs
  • CreateControlFile - src\Microsoft.DotNet.Build.Tasks.Installers\src\CreateControlFile.cs
  • CreateDebPackage - src\Microsoft.DotNet.Build.Tasks.Installers\src\CreateDebPackage.cs
  • CreateLightCommandPackageDrop - src\Microsoft.DotNet.Build.Tasks.Installers\src\CreateLightCommandPackageDrop.cs
  • CreateMD5SumsFile - src\Microsoft.DotNet.Build.Tasks.Installers\src\CreateMD5SumsFile.cs
  • CreateRpmPackage - src\Microsoft.DotNet.Build.Tasks.Installers\src\CreateRpmPackage.cs
  • CreateWixBuildWixpack - src\Microsoft.DotNet.Build.Tasks.Installers\src\CreateWixBuildWixpack.cs
  • ExecWithRetries - src\Microsoft.DotNet.Build.Tasks.Installers\src\ExecWithRetries.cs
  • GenerateCurrentVersion - src\Microsoft.DotNet.Build.Tasks.Installers\src\GenerateCurrentVersion.cs
  • GenerateGuidFromName - src\Microsoft.DotNet.Build.Tasks.Installers\src\GenerateGuidFromName.cs
  • GenerateMacOSDistributionFile - src\Microsoft.DotNet.Build.Tasks.Installers\src\GenerateMacOSDistributionFile.cs
  • GenerateMsiVersion - src\Microsoft.DotNet.Build.Tasks.Installers\src\GenerateMsiVersion.cs
  • StabilizeWixFileId - src\Microsoft.DotNet.Build.Tasks.Installers\src\StabilizeWixFileId.cs
Microsoft.DotNet.Build.Tasks.Packaging (27)
  • ApplyBaseLine - src\Microsoft.DotNet.Build.Tasks.Packaging\src\ApplyBaseLine.cs
  • ApplyMetaPackages - src\Microsoft.DotNet.Build.Tasks.Packaging\src\ApplyMetaPackages.cs
  • CreateTrimDependencyGroups - src\Microsoft.DotNet.Build.Tasks.Packaging\src\CreateTrimDependencyGroups.cs
  • FilterUnknownPackages - src\Microsoft.DotNet.Build.Tasks.Packaging\src\FilterUnknownPackages.cs
  • GenerateNuSpec - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GenerateNuSpec.cs
  • GenerateRuntimeDependencies - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GenerateRuntimeDependencies.cs
  • GetApplicableAssetsFromPackageReports - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetApplicableAssetsFromPackageReports.cs
  • GetApplicableAssetsFromPackages - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetApplicableAssetsFromPackages.cs
  • GetAssemblyReferences - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetAssemblyReferences.cs
  • GetInboxFrameworks - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetInboxFrameworks.cs
  • GetLayoutFiles - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetLayoutFiles.cs
  • GetPackageDescription - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetPackageDescription.cs
  • GetPackageDestination - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetPackageDestination.cs
  • GetPackageFromModule - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetPackageFromModule.cs
  • GetPackageVersion - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetPackageVersion.cs
  • GetRuntimeJsonValues - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetRuntimeJsonValues.cs
  • GetRuntimeTargets - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetRuntimeTargets.cs
  • GetSupportedPackagesFromPackageReports - src\Microsoft.DotNet.Build.Tasks.Packaging\src\GetSupportedPackagesFromPackageReports.cs
  • HarvestPackage - src\Microsoft.DotNet.Build.Tasks.Packaging\src\HarvestPackage.cs
  • NuGetPack - src\Microsoft.DotNet.Build.Tasks.Packaging\src\NuGetPack.cs
  • PromoteDependencies - src\Microsoft.DotNet.Build.Tasks.Packaging\src\PromoteDependencies.cs
  • SplitDependenciesBySupport - src\Microsoft.DotNet.Build.Tasks.Packaging\src\SplitDependenciesBySupport.cs
  • SplitReferences - src\Microsoft.DotNet.Build.Tasks.Packaging\src\SplitReferences.cs
  • UpdatePackageIndex - src\Microsoft.DotNet.Build.Tasks.Packaging\src\UpdatePackageIndex.cs
  • ValidateFrameworkPackage - src\Microsoft.DotNet.Build.Tasks.Packaging\src\ValidateFrameworkPackage.cs
  • ValidateHarvestVersionIsLatestForRelease - src\Microsoft.DotNet.Build.Tasks.Packaging\src\ValidateHarvestVersionIsLatestForRelease.cs
  • ValidatePackage - src\Microsoft.DotNet.Build.Tasks.Packaging\src\ValidatePackage.cs
Microsoft.DotNet.Build.Tasks.Templating (1)
  • GenerateFileFromTemplate - src\Microsoft.DotNet.Build.Tasks.Templating\src\GenerateFileFromTemplate.cs
Microsoft.DotNet.Build.Tasks.VisualStudio (6)
  • FinalizeInsertionVsixFile - src\Microsoft.DotNet.Build.Tasks.VisualStudio\Vsix\FinalizeInsertionVsixFile.cs
  • FindLatestDrop - src\Microsoft.DotNet.Build.Tasks.VisualStudio\OptProf\FindLatestDrop.cs
  • GenerateTrainingInputFiles - src\Microsoft.DotNet.Build.Tasks.VisualStudio\OptProf\GenerateTrainingInputFiles.cs
  • GenerateTrainingPropsFile - src\Microsoft.DotNet.Build.Tasks.VisualStudio\OptProf\GenerateTrainingPropsFile.cs
  • GetPkgDefAssemblyDependencyGuid - src\Microsoft.DotNet.Build.Tasks.VisualStudio\Vsix\GetPkgDefAssemblyDependencyGuid.cs
  • GetRunSettingsSessionConfiguration - src\Microsoft.DotNet.Build.Tasks.VisualStudio\OptProf\GetRunSettingsSessionConfiguration.cs
Microsoft.DotNet.Build.Tasks.Workloads (4)
  • CreateVisualStudioWorkload - src\Microsoft.DotNet.Build.Tasks.Workloads\src\CreateVisualStudioWorkload.wix.cs
  • CreateVisualStudioWorkloadSet - src\Microsoft.DotNet.Build.Tasks.Workloads\src\CreateVisualStudioWorkloadSet.wix.cs
  • HarvesterToolTask - src\Microsoft.DotNet.Build.Tasks.Workloads\src\Wix\HarvesterToolTask.cs
  • WixToolTask - src\Microsoft.DotNet.Build.Tasks.Workloads\src\Wix\WixToolTask.cs
Microsoft.DotNet.CMake.Sdk (2)
  • CreateCMakeFileApiQuery - src\Microsoft.DotNet.CMake.Sdk\src\CreateCMakeFileApiQuery.cs
  • GetCMakeArtifactsFromFileApi - src\Microsoft.DotNet.CMake.Sdk\src\GetCMakeArtifactsFromFileApi.cs
Microsoft.DotNet.Deployment.Tasks.Links (2)
  • CreateAkaMSLinks - src\Microsoft.DotNet.Deployment.Tasks.Links\src\CreateAkaMSLinks.cs
  • DeleteAkaMSLinks - src\Microsoft.DotNet.Deployment.Tasks.Links\src\DeleteAkaMSLinks.cs
Microsoft.DotNet.GenAPI (1)
  • GenAPITask - src\Microsoft.DotNet.GenAPI\GenAPITask.cs
Microsoft.DotNet.GenFacades (3)
  • ClearAssemblyReferenceVersions - src\Microsoft.DotNet.GenFacades\ClearAssemblyReferenceVersions.cs
  • GenPartialFacadeSource - src\Microsoft.DotNet.GenFacades\GenPartialFacadeSource.cs
  • NotSupportedAssemblyGenerator - src\Microsoft.DotNet.GenFacades\NotSupportedAssemblyGenerator.cs
Microsoft.DotNet.Helix (16)
  • CancelHelixJobs - src\Microsoft.DotNet.Helix\Sdk\CancelHelixJob.cs
  • CheckAzurePipelinesTestResults - src\Microsoft.DotNet.Helix\Sdk\CheckAzurePipelinesTestResults.cs
  • CheckHelixJobStatus - src\Microsoft.DotNet.Helix\Sdk\CheckHelixJobStatus.cs
  • CreateMTPWorkItems - src\Microsoft.DotNet.Helix\Sdk\CreateMTPWorkItems.cs
  • CreateTestsForWorkItems - src\Microsoft.DotNet.Helix\Sdk\CreateFailedTestsForFailedWorkItems.cs
  • CreateXHarnessAndroidWorkItems - src\Microsoft.DotNet.Helix\Sdk\CreateXHarnessAndroidWorkItems.cs
  • CreateXHarnessAppleWorkItems - src\Microsoft.DotNet.Helix\Sdk\CreateXHarnessAppleWorkItems.cs
  • CreateXUnitWorkItems - src\Microsoft.DotNet.Helix\Sdk\CreateXUnitWorkItems.cs
  • DownloadFromResultsContainer - src\Microsoft.DotNet.Helix\Sdk\DownloadFromResultsContainer.cs
  • FindDotNetCliPackage - src\Microsoft.DotNet.Helix\Sdk\FindDotNetCliPackage.cs
  • GetHelixWorkItems - src\Microsoft.DotNet.Helix\Sdk\GetHelixWorkItems.cs
  • InstallDotNetTool - src\Microsoft.DotNet.Helix\Sdk\InstallDotNetTool.cs
  • SendHelixJob - src\Microsoft.DotNet.Helix\Sdk\SendHelixJob.cs
  • StartAzurePipelinesTestRun - src\Microsoft.DotNet.Helix\Sdk\StartAzurePipelinesTestRun.cs
  • StopAzurePipelinesTestRun - src\Microsoft.DotNet.Helix\Sdk\StopAzurePipelinesTestRun.cs
  • WaitForHelixJobCompletion - src\Microsoft.DotNet.Helix\Sdk\WaitForHelixJobCompletion.cs
Microsoft.DotNet.NuGetRepack (2)
  • ReplacePackageParts - src\Microsoft.DotNet.NuGetRepack\tasks\src\ReplacePackageParts.cs
  • UpdatePackageVersionTask - src\Microsoft.DotNet.NuGetRepack\tasks\src\UpdatePackageVersionTask.cs
Microsoft.DotNet.PackageTesting (3)
  • GetCompatiblePackageTargetFrameworks - src\Microsoft.DotNet.PackageTesting\GetCompatiblePackageTargetFrameworks.cs
  • VerifyClosure - src\Microsoft.DotNet.PackageTesting\VerifyClosure.cs
  • VerifyTypes - src\Microsoft.DotNet.PackageTesting\VerifyTypes.cs
Microsoft.DotNet.SharedFramework.Sdk (5)
  • CreateFrameworkListFile - src\Microsoft.DotNet.SharedFramework.Sdk\src\CreateFrameworkListFile.cs
  • GeneratePlatformManifestEntriesFromFileList - src\Microsoft.DotNet.SharedFramework.Sdk\src\GeneratePlatformManifestEntriesFromFileList.cs
  • GeneratePlatformManifestEntriesFromTemplate - src\Microsoft.DotNet.SharedFramework.Sdk\src\GeneratePlatformManifestEntriesFromTemplate.cs
  • GenerateSharedFrameworkDepsFile - src\Microsoft.DotNet.SharedFramework.Sdk\src\GenerateSharedFrameworkDepsFile.cs
  • ValidateFileVersions - src\Microsoft.DotNet.SharedFramework.Sdk\src\ValidateFileVersions.cs
Microsoft.DotNet.SignCheckTask (1)
  • SignCheckTask - src\Microsoft.DotNet.SignCheckTask\SignCheckTask.cs
Microsoft.DotNet.SignTool (1)
  • SignToolTask - src\Microsoft.DotNet.SignTool\src\SignToolTask.cs
Microsoft.DotNet.SourceBuild (4)
  • ReadNuGetPackageInfos - src\Microsoft.DotNet.SourceBuild\tasks\src\ReadNuGetPackageInfos.cs
  • WriteBuildOutputProps - src\Microsoft.DotNet.SourceBuild\tasks\src\WriteBuildOutputProps.cs
  • WritePackageUsageData - src\Microsoft.DotNet.SourceBuild\tasks\src\UsageReport\WritePackageUsageData.cs
  • WriteUsageReports - src\Microsoft.DotNet.SourceBuild\tasks\src\UsageReport\WriteUsageReports.cs
Microsoft.DotNet.SwaggerGenerator (1)
  • GenerateSwaggerCode - src\Microsoft.DotNet.SwaggerGenerator\Microsoft.DotNet.SwaggerGenerator.MSBuild\GenerateSwaggerCode.cs
Microsoft.DotNet.XliffTasks (6)
  • EnsureAllResourcesTranslated - src\Microsoft.DotNet.XliffTasks\Tasks\EnsureAllResourcesTranslated.cs
  • GatherTranslatedSource - src\Microsoft.DotNet.XliffTasks\Tasks\GatherTranslatedSource.cs
  • GatherXlf - src\Microsoft.DotNet.XliffTasks\Tasks\GatherXlf.cs
  • SortXlf - src\Microsoft.DotNet.XliffTasks\Tasks\SortXlf.cs
  • TransformTemplates - src\Microsoft.DotNet.XliffTasks\Tasks\TransformTemplates.cs
  • UpdateXlf - src\Microsoft.DotNet.XliffTasks\Tasks\UpdateXlf.cs

/cc @dotnet/dnceng

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions