Add spec document and implement File I/O task migration for multithreaded MSBuild#7
Draft
Add spec document and implement File I/O task migration for multithreaded MSBuild#7
Conversation
…n status Co-authored-by: JanProvaznik <25267098+JanProvaznik@users.noreply.github.com>
…s section Co-authored-by: JanProvaznik <25267098+JanProvaznik@users.noreply.github.com>
Copilot
AI
changed the title
[WIP] Create spec document for threading fixes and logic updates
Add spec document: Threading fixes and task migration status for multithreaded MSBuild
Dec 3, 2025
JanProvaznik
requested changes
Dec 5, 2025
Comment on lines
+310
to
+338
| 1. **Copy** (`src/Tasks/Copy.cs`) | ||
| - Implements `IMultiThreadableTask` | ||
| - Uses `TaskEnvironment.GetAbsolutePath()` for source and destination paths | ||
| - Uses `TaskEnvironment.GetEnvironmentVariable()` for configuration checks | ||
| - Passes `TaskEnvironment` to `FileState` constructors | ||
|
|
||
| 2. **Delete** (`src/Tasks/Delete.cs`) | ||
| - Implements `IMultiThreadableTask` | ||
| - Uses `TaskEnvironment` for path resolution | ||
|
|
||
| 3. **MakeDir** (`src/Tasks/MakeDir.cs`) | ||
| - Implements `IMultiThreadableTask` | ||
| - Uses `TaskEnvironment` for directory path resolution | ||
|
|
||
| 4. **RemoveDir** (`src/Tasks/RemoveDir.cs`) | ||
| - Implements `IMultiThreadableTask` | ||
| - Uses `TaskEnvironment` for directory operations | ||
|
|
||
| 5. **Touch** (`src/Tasks/Touch.cs`) | ||
| - Implements `IMultiThreadableTask` | ||
| - Uses `TaskEnvironment` for file path resolution | ||
|
|
||
| 6. **ReadLinesFromFile** (`src/Tasks/FileIO/ReadLinesFromFile.cs`) | ||
| - Implements `IMultiThreadableTask` | ||
| - Uses `TaskEnvironment` for file access | ||
|
|
||
| 7. **WriteLinesToFile** (`src/Tasks/FileIO/WriteLinesToFile.cs`) | ||
| - Implements `IMultiThreadableTask` | ||
| - Uses `TaskEnvironment` for file writing |
Owner
There was a problem hiding this comment.
create changes on this branch for file iO task migration. Note that you need both the attribute and the interface to work on main
Author
There was a problem hiding this comment.
Implemented File I/O task migration with both MSBuildMultiThreadableTaskAttribute and IMultiThreadableTask interface.
Changes in commit 645bfec:
- Updated
FileState.csto acceptTaskEnvironmentparameter - Migrated 7 File I/O tasks: Copy, Delete, MakeDir, RemoveDir, Touch, ReadLinesFromFile, WriteLinesToFile
- Each task has the
[MSBuildMultiThreadableTask]attribute for routing - Each task implements
IMultiThreadableTaskforTaskEnvironmentaccess - All tasks use
TaskEnvironment.GetAbsolutePath()for thread-safe path resolution - Copy task also uses
TaskEnvironment.GetEnvironmentVariable()for environment access
All changes build successfully.
- Updated FileState.cs to accept TaskEnvironment parameter - Migrated 7 File I/O tasks to IMultiThreadableTask: * Copy - added attribute, interface, TaskEnvironment property and updated path/environment access * Delete - migrated with proper path resolution * MakeDir - migrated with path deduplication * RemoveDir - migrated with directory operations * Touch - migrated with file timestamp operations * ReadLinesFromFile - migrated for file reading * WriteLinesToFile - migrated for file writing - All tasks use MSBuildMultiThreadableTaskAttribute for routing - All tasks implement IMultiThreadableTask for TaskEnvironment access - Tasks use TaskEnvironment.GetAbsolutePath() for thread-safe path resolution - Tasks use TaskEnvironment.GetEnvironmentVariable() for thread-safe environment access Co-authored-by: JanProvaznik <25267098+JanProvaznik@users.noreply.github.com>
Copilot
AI
changed the title
Add spec document: Threading fixes and task migration status for multithreaded MSBuild
Add spec document and implement File I/O task migration for multithreaded MSBuild
Dec 5, 2025
…th illegal path characters (dotnet#12841) ### Context `/getItem` and `/getTargetResult` throw an unhandled `InvalidOperationException` when serializing items whose item specs contain illegal path characters (e.g., compiler command line flags from ClangTidy's `GetCompileCommands` target). The JSON output formatter iterates over all metadata names including built-in metadata like `FullPath`, `Directory`, etc. When `GetMetadata()` is called on these, it attempts path computation which fails for non-path item specs. ### Changes Made - Wrapped metadata retrieval in `JsonOutputFormatter.cs` with try-catch for `InvalidOperationException` - Added `TryGetMetadata` and `TryGetMetadataValue` helper methods that return empty string on failure - Applied fix to all three affected methods: `AddTargetResultsInJsonFormat`, `AddItemInstancesInJsonFormat`, `AddItemsInJsonFormat` - Exception handling catches all `InvalidOperationException` instances to ensure compatibility across all locales (error messages are localized) ### Testing Added `GetTargetResultWithIllegalPathCharacters` test that verifies both `/getTargetResult` and `/getItem` succeed with items containing compiler flags as item specs. ### Notes On Linux, the test shows the path-like metadata is still computed (since `/` is valid in paths). On Windows, these would return empty strings. The key fix is preventing the unhandled exception crash. The exception handling is intentionally broad (catching all `InvalidOperationException` without message filtering) to ensure the fix works correctly in all locales, as error messages from MSBuild are localized and checking for specific English text would fail in non-English environments. <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> ---- *This section details on the original issue you should resolve* <issue_title>[Unhandled Exception]: /getItem and /getTargetResult fail for target GetCompileCommands</issue_title> <issue_description>### Issue Description There is an uncaught exception when trying to access certain information from the GetCompileCommands target (from ClangTidy), in particular `/getTargetResult:GetCompileCommands` and `/getTargetResult:GetCompileCommands` ### Steps to Reproduce [tidytest.zip](https://github.com/user-attachments/files/22645381/tidytest.zip) Run either of the following commands (You could also run with target `/t:ClangTidy` for the same result) ``` msbuild tidytest.vcxproj /t:GetCompileCommands /getTargetResult:GetCompileCommands msbuild tidytest.vcxproj /t:GetCompileCommands /getItem:CompileCommands ``` I expect to get a json formatted target result or item. ### Actual Behavior I get the following error when building ``` C:\Users\rp0656\source\repos\tidytest>msbuild tidytest.vcxproj /t:GetCompileCommands /getTargetResult:GetCompileCommands MSBUILD : error MSB1025: An internal failure occurred while running MSBuild. System.InvalidOperationException: The item metadata "%(FullPath)" cannot be applied to the path "/c /nologo /W3 /WX- /diagnostics:column /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /EHsc /RTC1 /MDd /GS /fp:precise /permissive- /Fa"tidytest\x64\Debug\\" /Fo"tidytest\x64\Debug\\" /Gd --target=amd64-pc-windows-msvc /TP". Illegal characters in path. at Microsoft.Build.Shared.ErrorUtilities.ThrowInvalidOperation(String resourceName, Object[] args) at Microsoft.Build.Shared.FileUtilities.ItemSpecModifiers.GetItemSpecModifier(String currentDirectory, String itemSpec, String definingProjectEscaped, String modifier, String& fullPath) at Microsoft.Build.Evaluation.BuiltInMetadata.GetMetadataValueEscaped(String currentDirectory, String evaluatedIncludeBeforeWildcardExpansionEscaped, String evaluatedIncludeEscaped, String definingProjectEscaped, String name, String& fullPath) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetBuiltInMetadataEscaped(String name) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetMetadataEscaped(String metadataName) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetMetadata(String metadataName) at Microsoft.Build.CommandLine.JsonOutputFormatter.AddTargetResultsInJsonFormat(String[] targetNames, BuildResult result) at Microsoft.Build.CommandLine.MSBuildApp.OutputBuildInformationInJson(BuildResult result, String[] getProperty, String[] getItem, String[] getTargetResult, ILogger[] loggers, ExitType exitType, TextWriter outputStream) at Microsoft.Build.CommandLine.MSBuildApp.Execute(String commandLine) Unhandled Exception: System.InvalidOperationException: The item metadata "%(FullPath)" cannot be applied to the path "/c /nologo /W3 /WX- /diagnostics:column /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /EHsc /RTC1 /MDd /GS /fp:precise /permissive- /Fa"tidytest\x64\Debug\\" /Fo"tidytest\x64\Debug\\" /Gd --target=amd64-pc-windows-msvc /TP". Illegal characters in path. at Microsoft.Build.Shared.ErrorUtilities.ThrowInvalidOperation(String resourceName, Object[] args) at Microsoft.Build.Shared.FileUtilities.ItemSpecModifiers.GetItemSpecModifier(String currentDirectory, String itemSpec, String definingProjectEscaped, String modifier, String& fullPath) at Microsoft.Build.Evaluation.BuiltInMetadata.GetMetadataValueEscaped(String currentDirectory, String evaluatedIncludeBeforeWildcardExpansionEscaped, String evaluatedIncludeEscaped, String definingProjectEscaped, String name, String& fullPath) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetBuiltInMetadataEscaped(String name) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetMetadataEscaped(String metadataName) at Microsoft.Build.Execution.ProjectItemInstance.TaskItem.GetMetadata(String metadataName) at Microsoft.Build.CommandLine.JsonOutputFormatter.AddTargetResultsInJsonFormat(String[] targetNames, BuildResult result) at Microsoft.Build.CommandLine.MSBuildApp.OutputBuildInformationInJson(BuildResult result, String[] getProperty, String[] getItem, String[] getTargetResult, ILogger[] loggers, ExitType exitType, TextWriter outputStream) at Microsoft.Build.CommandLine.MSBuildApp.Execute(String commandLine) at Microsoft.Build.CommandLine.MSBuildApp.Main() ``` ### Analysis The error suggests that it is trying to interpret the flags of the compile commands as a path, which causes an error when it can't be resolved. The function `%(FullPath)` is used, though it is not clear to me exactly where, but my best guess is it used on a variable that is supposed to just be the path to `cl`, but has been extended with flags. See the clang tidy targets file at something similar to `C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VC\v170\Microsoft.Cpp.ClangTidy.targets` ### Versions & Co... </details> - Fixes dotnet#12589 <!-- START COPILOT CODING AGENT TIPS --> --- ✨ Let Copilot coding agent [set things up for you](https://github.com/dotnet/msbuild/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JanProvaznik <25267098+JanProvaznik@users.noreply.github.com> Co-authored-by: Jan Provazník <janprovaznik@microsoft.com> Co-authored-by: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com>
…#12918) ### Context Structured log viewer was cluttered with repeated non-actionable messages about SDK environment variables (e.g., `DOTNET_HOST_PATH`). These appeared for every project evaluation, reporting normal SDK behavior without providing value to users. ### Changes Made Modified `ProjectInstance.AddSdkResolvedEnvironmentVariable()` to only log messages when an SDK attempts to set an environment variable to a **different value** than what's already set: - **When values match** (common case): No message is logged, eliminating the clutter shown in the original issue - **When values differ** (conflict): A low-importance message is logged showing both the attempted value and the existing value for diagnostic purposes - **Code refactoring**: Extracted duplicate value comparison and logging logic into a helper method `LogIfValueDiffers` to improve code maintainability Updated string resources in `Strings.resx` and all 13 localized `.xlf` files: - `SdkEnvironmentVariableAlreadySet`: Now includes both attempted and existing values in the message - `SdkEnvironmentVariableAlreadySetBySdk`: Now includes both attempted and existing values in the message - Removed `SdkEnvironmentVariableSet` (no longer logging successful sets) The underlying functionality is unchanged—environment variables are still tracked and set correctly, and properties remain visible in the structured log viewer. ### Testing - Built MSBuild successfully - Verified messages no longer appear when values are the same (common case) - Messages would appear when values differ (preserves diagnostic value for conflicts) - Confirmed existing unit tests pass - Verified sample projects build successfully - Confirmed properties are still visible in structured log viewer (DOTNET_HOST_PATH, etc.) ### Notes This implementation balances two concerns: 1. **Eliminates noise**: Repeated messages about the same value being set multiple times no longer clutter logs 2. **Preserves diagnostics**: Actual conflicts (different values) are still logged with full context (both values shown) The messages log at `MessageImportance.Low` to provide diagnostic information without being intrusive in normal builds. Code quality was improved by extracting duplicate logic into a reusable helper method. <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Duplicated messages from SDK/environment resolution</issue_title> > <issue_description><img width="2860" height="1772" alt="Image" src="https://github.com/user-attachments/assets/f4d4871f-28e9-4cb5-9f58-42c4a0056979" /> > > There are a ton of these `An SDK attempted to set the environment variable "DOTNET_HOST_PATH"` messages that aren't particularly helpful, and they're not attributed to a project so they clutter up the top level of the viewer tool.</issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes dotnet#12915 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com>
…: Build ID 13036709
…: Build ID 13036709 (dotnet#12975) This is the pull request automatically created by the OneLocBuild task in the build process to check-in localized files generated based upon translation source files (.lcl files) handed-back from the downstream localization pipeline. If there are issues in translations, visit https://aka.ms/icxLocBug and log bugs for fixes. The OneLocBuild wiki is https://aka.ms/onelocbuild and the localization process in general is documented at https://aka.ms/AllAboutLoc.
I detected changes in the vs18.3 branch which have not been merged yet to main. I'm a robot and am configured to help you automatically keep main up to date, so I've opened this PR. This PR merges commits made on vs18.3 by the following committers: * @github-actions[bot] * @dotnet-maestro[bot] ## Instructions for merging from UI This PR will not be auto-merged. When pull request checks pass, complete this PR by creating a merge commit, *not* a squash or rebase commit. <img alt="merge button instructions" src="https://i.imgur.com/GepcNJV.png" width="300" /> If this repo does not allow creating merge commits from the GitHub UI, use command line instructions. ## Instructions for merging via command line Run these commands to merge this pull request from the command line. ``` sh git fetch git checkout vs18.3 git pull --ff-only git checkout main git pull --ff-only git merge --no-ff vs18.3 # If there are merge conflicts, resolve them and then run git merge --continue to complete the merge # Pushing the changes to the PR branch will re-trigger PR validation. git push https://github.com/dotnet/msbuild HEAD:merge/vs18.3-to-main ``` <details> <summary>or if you are using SSH</summary> ``` git push git@github.com:dotnet/msbuild HEAD:merge/vs18.3-to-main ``` </details> After PR checks are complete push the branch ``` git push ``` ## Instructions for resolving conflicts :warning: If there are merge conflicts, you will need to resolve them manually before merging. You can do this [using GitHub][resolve-github] or using the [command line][resolve-cli]. [resolve-github]: https://help.github.com/articles/resolving-a-merge-conflict-on-github/ [resolve-cli]: https://help.github.com/articles/resolving-a-merge-conflict-using-the-command-line/ ## Instructions for updating this pull request Contributors to this repo have permission update this pull request by pushing to the branch 'merge/vs18.3-to-main'. This can be done to resolve conflicts or make other changes to this pull request before it is merged. The provided examples assume that the remote is named 'origin'. If you have a different remote name, please replace 'origin' with the name of your remote. ``` git fetch git checkout -b merge/vs18.3-to-main origin/main git pull https://github.com/dotnet/msbuild merge/vs18.3-to-main (make changes) git commit -m "Updated PR with my changes" git push https://github.com/dotnet/msbuild HEAD:merge/vs18.3-to-main ``` <details> <summary>or if you are using SSH</summary> ``` git fetch git checkout -b merge/vs18.3-to-main origin/main git pull git@github.com:dotnet/msbuild merge/vs18.3-to-main (make changes) git commit -m "Updated PR with my changes" git push git@github.com:dotnet/msbuild HEAD:merge/vs18.3-to-main ``` </details> Contact .NET Core Engineering (dotnet/dnceng) if you have questions or issues. Also, if this PR was generated incorrectly, help us fix it. See https://github.com/dotnet/arcade/blob/main/.github/workflows/scripts/inter-branch-merge.ps1.
Co-authored-by: AR-May <67507805+AR-May@users.noreply.github.com>
…://github.com/JanProvaznik/msbuild into copilot/outline-threading-fixes-logic-updates
In scope of dotnet#12753 support for translating HostServices in Out of Proc Task Host was added, but limited to NET task host. This change removes this limitation.
I detected changes in the vs18.3 branch which have not been merged yet to main. I'm a robot and am configured to help you automatically keep main up to date, so I've opened this PR. This PR merges commits made on vs18.3 by the following committers: * @ViktorHofer ## Instructions for merging from UI This PR will not be auto-merged. When pull request checks pass, complete this PR by creating a merge commit, *not* a squash or rebase commit. <img alt="merge button instructions" src="https://i.imgur.com/GepcNJV.png" width="300" /> If this repo does not allow creating merge commits from the GitHub UI, use command line instructions. ## Instructions for merging via command line Run these commands to merge this pull request from the command line. ``` sh git fetch git checkout vs18.3 git pull --ff-only git checkout main git pull --ff-only git merge --no-ff vs18.3 # If there are merge conflicts, resolve them and then run git merge --continue to complete the merge # Pushing the changes to the PR branch will re-trigger PR validation. git push https://github.com/dotnet/msbuild HEAD:merge/vs18.3-to-main ``` <details> <summary>or if you are using SSH</summary> ``` git push git@github.com:dotnet/msbuild HEAD:merge/vs18.3-to-main ``` </details> After PR checks are complete push the branch ``` git push ``` ## Instructions for resolving conflicts :warning: If there are merge conflicts, you will need to resolve them manually before merging. You can do this [using GitHub][resolve-github] or using the [command line][resolve-cli]. [resolve-github]: https://help.github.com/articles/resolving-a-merge-conflict-on-github/ [resolve-cli]: https://help.github.com/articles/resolving-a-merge-conflict-using-the-command-line/ ## Instructions for updating this pull request Contributors to this repo have permission update this pull request by pushing to the branch 'merge/vs18.3-to-main'. This can be done to resolve conflicts or make other changes to this pull request before it is merged. The provided examples assume that the remote is named 'origin'. If you have a different remote name, please replace 'origin' with the name of your remote. ``` git fetch git checkout -b merge/vs18.3-to-main origin/main git pull https://github.com/dotnet/msbuild merge/vs18.3-to-main (make changes) git commit -m "Updated PR with my changes" git push https://github.com/dotnet/msbuild HEAD:merge/vs18.3-to-main ``` <details> <summary>or if you are using SSH</summary> ``` git fetch git checkout -b merge/vs18.3-to-main origin/main git pull git@github.com:dotnet/msbuild merge/vs18.3-to-main (make changes) git commit -m "Updated PR with my changes" git push git@github.com:dotnet/msbuild HEAD:merge/vs18.3-to-main ``` </details> Contact .NET Core Engineering (dotnet/dnceng) if you have questions or issues. Also, if this PR was generated incorrectly, help us fix it. See https://github.com/dotnet/arcade/blob/main/.github/workflows/scripts/inter-branch-merge.ps1.
…: Build ID 13137486 (dotnet#13075) This is the pull request automatically created by the OneLocBuild task in the build process to check-in localized files generated based upon translation source files (.lcl files) handed-back from the downstream localization pipeline. If there are issues in translations, visit https://aka.ms/icxLocBug and log bugs for fixes. The OneLocBuild wiki is https://aka.ms/onelocbuild and the localization process in general is documented at https://aka.ms/AllAboutLoc.
…#13079) ### Context PRs dotnet#12914 and dotnet#12868 showed that we need to be able to fix and normalize `AbsolutePath`. Corresponding methods for strings live in `FileUtilities.cs` that is a Shared file and cannot reference Framework classes like `AbsolutePath`. ### Changes Made - Moved some related functions to FrameworkFileUtilities class. - Added the needed functions for `AbsolutePath` ### Testing unit tests
Fixes # VS insertion: https://dev.azure.com/devdiv/DevDiv/_git/VS/pullRequest/701704#1768990369 C++ projects use clr2 for DTB and this change dotnet#13033 causes issues since an extended flag support wasn't added to the legacy environment by design. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This pull request updates the following dependencies [marker]: <> (Begin:e61ebb2e-41b3-4d00-9d65-5ff5253f9a59) ## From https://github.com/nuget/nuget.client - **Subscription**: [e61ebb2e-41b3-4d00-9d65-5ff5253f9a59](https://maestro.dot.net/subscriptions?search=e61ebb2e-41b3-4d00-9d65-5ff5253f9a59) - **Build**: [7.4.0.12](https://dev.azure.com/devdiv/DevDiv/_build/results?buildId=13118225) ([297707](https://maestro.dot.net/channel/8706/github:nuget:nuget.client/build/297707)) - **Date Produced**: January 17, 2026 3:46:03 AM UTC - **Commit**: [367c40e337c0881682bc437b344511f482084d67](NuGet/NuGet.Client@367c40e) - **Branch**: [dev](https://github.com/nuget/nuget.client/tree/dev) [DependencyUpdate]: <> (Begin) - **Dependency Updates**: - From [7.3.0-preview.1.50 to 7.4.0-rc.12][1] - NuGet.Build.Tasks [1]: NuGet/NuGet.Client@a99b70c...367c40e [DependencyUpdate]: <> (End) [marker]: <> (End:e61ebb2e-41b3-4d00-9d65-5ff5253f9a59) Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
…: Build ID 13137774 (dotnet#13081) This is the pull request automatically created by the OneLocBuild task in the build process to check-in localized files generated based upon translation source files (.lcl files) handed-back from the downstream localization pipeline. If there are issues in translations, visit https://aka.ms/icxLocBug and log bugs for fixes. The OneLocBuild wiki is https://aka.ms/onelocbuild and the localization process in general is documented at https://aka.ms/AllAboutLoc.
Coordinated update with VS. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com>
JanProvaznik
pushed a commit
that referenced
this pull request
Mar 27, 2026
…tInMetadata performance (dotnet#13386) I recommend reviewing this pull request commit-by-commit. # Summary of Changes - Introduce new MSBuild.Benchmarks project for tackling performance investigations. - Add a few benchmarks for various methods on `ItemSpecModifiers`. - Make several performance fixes related to `ItemSpecModifiers` and `BuildInMetadata`. - General clean up. Across the board, most benchmarks are **3×–35× faster** with **allocations eliminated or reduced by 93–100%**. The largest wins are in repeated-access and multi-item scenarios, which are the most representative of real build workloads. The full details are below but here are the highlights. ## Highlights ### 🚀 Speed Improvements (.NET 10.0) | Benchmark | Before | After | Speedup | |---|---|---|---| | `IsItemSpecModifier_AllModifiers` | 151.4 ns | 38.5 ns | **3.9×** | | `GetItemSpecModifier_DefiningProjectDirectory` | 812.2 ns | 72.4 ns | **11.2×** | | `TaskItem_AllDerivableModifiers_Once` | 434.5 ns | 87.4 ns | **5.0×** | | `TaskItem_FilenameAndExtension_Repeated` | 908.5 ns | 173.7 ns | **5.2×** | | `TaskItem_Filename_ManyItems` | 9,651 ns | 1,857 ns | **5.2×** | | `TaskItem_FullPathDerivedModifiers_Repeated` | 2,128 ns | 321.6 ns | **6.6×** | | `TaskItem_DefiningProjectDirectory_Repeated` | 9,121 ns | 970.8 ns | **9.4×** | | `PI_DefiningProjectDirectory_Repeated` | 9,092 ns | 1,021 ns | **8.9×** | | `PI_DefiningProjectFullPath_AllItems_Multi` | 35,245 ns | 9,947 ns | **3.5×** | | `PI_DefiningProjectDir_AllItems_Multi_Repeated` | 878,931 ns | 103,961 ns | **8.5×** | | `PI_FilenameExtension_AllItems` | 22,698 ns | 5,343 ns | **4.2×** | | `PI_FilenameExtension_AllItems_Repeated` | 181,612 ns | 67,028 ns | **2.7×** | ### 🧹 Allocation Reductions (.NET 10.0) | Benchmark | Before | After | Reduction | |---|---|---|---| | `TaskItem_AllDerivableModifiers_Once` | 1,232 B | **0 B** | 100% | | `TaskItem_FilenameAndExtension_Repeated` | 640 B | **0 B** | 100% | | `TaskItem_Filename_ManyItems` | 7,920 B | **0 B** | 100% | | `TaskItem_FullPathDerivedModifiers_Repeated` | 7,120 B | **0 B** | 100% | | `TaskItem_DefiningProjectDirectory_Repeated` | 8,240 B | **0 B** | 100% | | `TaskItem_AllDefiningProjectModifiers_Once` | 912 B | **0 B** | 100% | | `TaskItem_DefiningProjectNameExtension_AllItems` | 8,800 B | **0 B** | 100% | | `GetItemSpecModifier_DefiningProjectDirectory` | 536 B | **0 B** | 100% | | `PI_FilenameExtension_AllItems_Repeated` | 143,840 B | **640 B** | 99.6% | | `PI_DefiningProjectDir_AllItems_Multi_Repeated` | 824,640 B | **640 B** | 99.9% | | `PI_FilenameExtension_AllItems` | 14,384 B | **64 B** | 99.6% | | `PI_DefiningProjectDirectory_Repeated` | 8,304 B | **64 B** | 99.2% | | `PI_AllDerivableModifiers_Once` | 1,296 B | **64 B** | 95.1% | | `PI_AllDefiningProjectModifiers_Once` | 976 B | **64 B** | 93.4% | ### 📊 .NET Framework 4.8.1 | Benchmark | Before | After | Speedup | |---|---|---|---| | `GetItemSpecModifier_DefiningProjectDirectory` | 5,467 ns | 156.8 ns | **34.9×** | | `TaskItem_DefiningProjectDirectory_Repeated` | 58,025 ns | 2,539 ns | **22.9×** | | `PI_DefiningProjectDir_AllItems_Multi_Repeated` | 6,399 μs | 282.3 μs | **22.7×** | | `TaskItem_Filename_ManyItems` | 110,078 ns | 6,916 ns | **15.9×** | | `TaskItem_FullPathDerivedModifiers_Repeated` | 26,619 ns | 2,262 ns | **11.8×** | | `PI_FilenameExtension_AllItems_Repeated` | 2,162 μs | 202.6 μs | **10.7×** | | `TaskItem_AllDerivableModifiers_Once` | 5,322 ns | 507.2 ns | **10.5×** | | `PI_FilenameExtension_AllItems` | 216,406 ns | 20,366 ns | **10.6×** | | `TaskItem_FilenameAndExtension_Repeated` | 10,238 ns | 664.7 ns | **15.4×** | | `PI_AllDerivableModifiers_Once` | 5,525 ns | 688.6 ns | **8.0×** | | `PI_DefiningProjectDirectory_Repeated` | 64,204 ns | 2,796 ns | **23.0×** | | `PI_AllDefiningProjectModifiers_Once` | 9,808 ns | 1,154 ns | **8.5×** | | `TaskItem_AllDefiningProjectModifiers_Once` | 8,636 ns | 965.3 ns | **8.9×** | ## 'Quick-and-Dirty' Telemetry I had Copilot write some "quick-and-dirty" telemetry to track information in `ItemSpecModifiers` and dump it to a file. I built MSBuild with that extra telemetry and then built Roslyn (starting from `Microsoft.VisualStudio.LanguageServices.CSharp.csproj`) using that MSBuild. This gave me a dump with loads of interesting details. For example, `IsItemSpecModifier` was called 901,057 times. <details> <summary><b>Full Quick-and-Dirty Telemetry Dump</b></summary> ``` === ItemSpecModifiers Telemetry (2026-03-12T09:38:41.0365991-07:00) === --- Top-level method calls --- IsItemSpecModifier: 901,057 IsDerivableItemSpecModifier: 144,857 GetItemSpecModifier: 180,890 --- Compute helper calls --- ComputeFullPath: 75,443 ComputeRootDir: 84 ComputeFilename: 57,426 ComputeExtension: 32,071 ComputeRelativeDir: 22 ComputeDirectory: 84 ComputeModifiedTime: 0 ComputeCreatedTime: 0 ComputeAccessedTime: 0 --- Per-modifier breakdown (inside GetItemSpecModifier) --- FullPath: 44,783 RootDir: 41 Filename: 57,426 Extension: 6,462 RelativeDir: 22 Directory: 41 RecursiveDir: 0 Identity: 15,814 ModifiedTime: 0 CreatedTime: 0 AccessedTime: 0 DefiningProjectFullPath: 30,535 DefiningProjectDirectory: 43 DefiningProjectName: 0 DefiningProjectExtension: 25,609 ========================================================== Per-ItemSpec Modifier Hit Matrix ========================================================== Unique item specs seen: 13,809 Item specs hit > 1 time: 13,005 Grand total modifier lookups: 180,890 --- Top 50 hottest item specs (by total modifier calls) --- #1 (1,502 calls): System.Runtime.CompilerServices.InternalsVisibleTo Identity 751 DefiningProjectFullPath 751 #2 (380 calls): D:\Projects\roslyn\artifacts\bin\Microsoft.CodeAnalysis\Debug\netstandard2.0\Microsoft.CodeAnalysis.dll FullPath 17 Filename 280 Extension 40 Identity 21 DefiningProjectFullPath 22 #3 (270 calls): D:\Projects\roslyn\artifacts\bin\Microsoft.CodeAnalysis.Scripting\Debug\netstandard2.0\Microsoft.CodeAnalysis.Scripting.dll FullPath 12 Filename 195 Extension 30 Identity 16 DefiningProjectFullPath 17 #4 (270 calls): D:\Projects\roslyn\artifacts\bin\Microsoft.CodeAnalysis.Workspaces\Debug\netstandard2.0\Microsoft.CodeAnalysis.Workspaces.dll FullPath 12 Filename 195 Extension 30 Identity 16 DefiningProjectFullPath 17 #5 (247 calls): D:\.nuget\packages\system.threading.tasks.extensions\4.6.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll FullPath 13 Filename 221 Identity 13 #6 (247 calls): D:\.nuget\packages\system.memory\4.6.3\lib\netstandard2.0\System.Memory.dll FullPath 13 Filename 221 Identity 13 #7 (247 calls): D:\.nuget\packages\system.buffers\4.6.1\lib\netstandard2.0\System.Buffers.dll FullPath 13 Filename 221 Identity 13 #8 (247 calls): D:\.nuget\packages\system.numerics.vectors\4.6.1\lib\netstandard2.0\System.Numerics.Vectors.dll FullPath 13 Filename 221 Identity 13 #9 (247 calls): D:\.nuget\packages\system.text.encoding.codepages\8.0.0\lib\netstandard2.0\System.Text.Encoding.CodePages.dll FullPath 13 Filename 221 Identity 13 #10 (247 calls): D:\.nuget\packages\system.runtime.compilerservices.unsafe\6.1.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll FullPath 13 Filename 221 Identity 13 #11 (245 calls): D:\.nuget\packages\microsoft.dotnet.arcade.sdk\10.0.0-beta.26160.1\tools\Assets\DotNetPackageIcon.png FullPath 48 Filename 40 Extension 70 Identity 2 DefiningProjectFullPath 37 DefiningProjectDirectory 8 DefiningProjectExtension 40 #12 (245 calls): D:\Projects\roslyn\eng\targets\..\..\src\NuGet\ThirdPartyNotices.rtf FullPath 48 Filename 40 Extension 70 Identity 2 DefiningProjectFullPath 37 DefiningProjectDirectory 8 DefiningProjectExtension 40 #13 (240 calls): D:\.nuget\packages\system.collections.immutable\10.0.1\lib\netstandard2.0\System.Collections.Immutable.dll FullPath 12 Filename 216 Identity 12 #14 (240 calls): D:\.nuget\packages\system.reflection.metadata\10.0.1\lib\netstandard2.0\System.Reflection.Metadata.dll FullPath 12 Filename 216 Identity 12 #15 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Web.dll FullPath 13 Filename 208 Identity 13 #16 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.ThreadPool.dll FullPath 13 Filename 208 Identity 13 #17 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.Watcher.dll FullPath 13 Filename 208 Identity 13 #18 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Resources.ResourceManager.dll FullPath 13 Filename 208 Identity 13 #19 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.dll FullPath 13 Filename 208 Identity 13 dotnet#20 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Pipes.dll FullPath 13 Filename 208 Identity 13 dotnet#21 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.MemoryMappedFiles.dll FullPath 13 Filename 208 Identity 13 dotnet#22 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ValueTuple.dll FullPath 13 Filename 208 Identity 13 dotnet#23 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Console.dll FullPath 13 Filename 208 Identity 13 dotnet#24 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Parallel.dll FullPath 13 Filename 208 Identity 13 dotnet#25 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Claims.dll FullPath 13 Filename 208 Identity 13 dotnet#26 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.UnmanagedMemoryStream.dll FullPath 13 Filename 208 Identity 13 dotnet#27 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Threading.Overlapped.dll FullPath 13 Filename 208 Identity 13 dotnet#28 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Drawing.Primitives.dll FullPath 13 Filename 208 Identity 13 dotnet#29 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.WebSockets.dll FullPath 13 Filename 208 Identity 13 dotnet#30 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Algorithms.dll FullPath 13 Filename 208 Identity 13 dotnet#31 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.FileSystem.DriveInfo.dll FullPath 13 Filename 208 Identity 13 dotnet#32 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Diagnostics.Tracing.dll FullPath 13 Filename 208 Identity 13 dotnet#33 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Linq.Expressions.dll FullPath 13 Filename 208 Identity 13 dotnet#34 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Principal.dll FullPath 13 Filename 208 Identity 13 dotnet#35 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Globalization.Calendars.dll FullPath 13 Filename 208 Identity 13 dotnet#36 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.Primitives.dll FullPath 13 Filename 208 Identity 13 dotnet#37 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ObjectModel.dll FullPath 13 Filename 208 Identity 13 dotnet#38 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XDocument.dll FullPath 13 Filename 208 Identity 13 dotnet#39 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.dll FullPath 13 Filename 208 Identity 13 dotnet#40 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.ComponentModel.TypeConverter.dll FullPath 13 Filename 208 Identity 13 dotnet#41 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.InteropServices.dll FullPath 13 Filename 208 Identity 13 dotnet#42 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Concurrent.dll FullPath 13 Filename 208 Identity 13 dotnet#43 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Collections.Specialized.dll FullPath 13 Filename 208 Identity 13 dotnet#44 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Dynamic.Runtime.dll FullPath 13 Filename 208 Identity 13 dotnet#45 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.Requests.dll FullPath 13 Filename 208 Identity 13 dotnet#46 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Security.Cryptography.X509Certificates.dll FullPath 13 Filename 208 Identity 13 dotnet#47 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Net.dll FullPath 13 Filename 208 Identity 13 dotnet#48 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.IO.Compression.ZipFile.dll FullPath 13 Filename 208 Identity 13 dotnet#49 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Xml.XmlSerializer.dll FullPath 13 Filename 208 Identity 13 dotnet#50 (234 calls): D:\.nuget\packages\netstandard.library\2.0.3\build\netstandard2.0\ref\System.Runtime.CompilerServices.VisualC.dll FullPath 13 Filename 208 Identity 13 --- Repetition histogram (total modifier calls per item spec → count of item specs) --- 1,502 calls → 1 item specs 380 calls → 1 item specs 270 calls → 2 item specs 247 calls → 6 item specs 245 calls → 2 item specs 240 calls → 2 item specs 234 calls → 112 item specs 226 calls → 1 item specs 224 calls → 2 item specs 204 calls → 4 item specs 196 calls → 1 item specs 164 calls → 13 item specs 160 calls → 2 item specs 151 calls → 3 item specs 144 calls → 53 item specs 140 calls → 6 item specs 138 calls → 4 item specs 134 calls → 1 item specs 132 calls → 7 item specs 122 calls → 2 item specs 117 calls → 14 item specs 113 calls → 124 item specs 111 calls → 1 item specs 110 calls → 2 item specs 109 calls → 1 item specs 101 calls → 3 item specs 100 calls → 2 item specs 99 calls → 1 item specs 98 calls → 1 item specs 91 calls → 5 item specs 89 calls → 59 item specs 86 calls → 2 item specs 84 calls → 1 item specs 82 calls → 2 item specs 80 calls → 7 item specs 78 calls → 1 item specs 76 calls → 2 item specs 75 calls → 1 item specs 74 calls → 5 item specs 73 calls → 3 item specs 72 calls → 2 item specs 70 calls → 1 item specs 66 calls → 5 item specs 65 calls → 5 item specs 60 calls → 10 item specs 58 calls → 8 item specs 57 calls → 1 item specs 56 calls → 1 item specs 54 calls → 6 item specs 52 calls → 3 item specs 51 calls → 8 item specs 50 calls → 1 item specs 49 calls → 2 item specs 48 calls → 3 item specs 47 calls → 1 item specs 46 calls → 1 item specs 45 calls → 13 item specs 44 calls → 20 item specs 41 calls → 2 item specs 40 calls → 8 item specs 39 calls → 1 item specs 37 calls → 4 item specs 36 calls → 51 item specs 35 calls → 5 item specs 34 calls → 1 item specs 33 calls → 1 item specs 32 calls → 2 item specs 30 calls → 4 item specs 29 calls → 11 item specs 28 calls → 20 item specs 27 calls → 11 item specs 26 calls → 16 item specs 24 calls → 30 item specs 23 calls → 3 item specs 22 calls → 153 item specs 21 calls → 1 item specs 20 calls → 26 item specs 19 calls → 1 item specs 18 calls → 109 item specs 16 calls → 30 item specs 15 calls → 35 item specs 14 calls → 383 item specs 13 calls → 166 item specs 12 calls → 1,051 item specs 11 calls → 1 item specs 10 calls → 70 item specs 9 calls → 43 item specs 8 calls → 7,012 item specs 7 calls → 50 item specs 6 calls → 910 item specs 5 calls → 1,420 item specs 4 calls → 449 item specs 3 calls → 32 item specs 2 calls → 340 item specs 1 calls → 804 item specs ``` </details> ## Initial Benchmark Results ### .NET 10.0 | Method | Mean | Error | StdDev | Gen0 | Allocated | |--------------------------------------------- |--------------:|------------:|------------:|-------:|----------:| | IsItemSpecModifier_AllModifiers | 151.425 ns | 0.1309 ns | 0.1161 ns | - | - | | IsDerivableItemSpecModifier_RecursiveDir | 2.522 ns | 0.0028 ns | 0.0025 ns | - | - | | GetItemSpecModifier_FullPath | 267.552 ns | 0.3441 ns | 0.3050 ns | - | - | | GetItemSpecModifier_Directory | 359.023 ns | 0.7603 ns | 0.6740 ns | 0.0224 | 376 B | | GetItemSpecModifier_ModifiedTime | 28,544.061 ns | 237.0185 ns | 221.7073 ns | - | 176 B | | GetItemSpecModifier_DefiningProjectDirectory | 812.229 ns | 3.3636 ns | 2.9817 ns | 0.0315 | 536 B | | TaskItem_AllDerivableModifiers_Once | 434.5 ns | 4.36 ns | 4.08 ns | 0.0734 | 1232 B | | TaskItem_FilenameAndExtension_Repeated | 908.5 ns | 1.13 ns | 1.00 ns | 0.0381 | 640 B | | TaskItem_Filename_ManyItems | 9,651.3 ns | 29.02 ns | 25.72 ns | 0.4730 | 7920 B | | TaskItem_FullPathDerivedModifiers_Repeated | 2,127.6 ns | 7.45 ns | 6.60 ns | 0.4234 | 7120 B | | ProjectItemInstance_AllDerivableModifiers_Once | 513.4 ns | 2.93 ns | 2.74 ns | 0.0772 | 1296 B | | ProjectItemInstance_FilenameExtension_AllItems | 22,697.6 ns | 68.62 ns | 64.19 ns | 0.8545 | 14384 B | | ProjectItemInstance_FilenameExtension_AllItems_Repeated | 181,612.2 ns | 1,125.69 ns | 940.00 ns | 8.5449 | 143840 B | | ProjectItemInstance_AllDefiningProjectModifiers_Once | 1.559 μs | 0.0047 μs | 0.0040 μs | 0.0572 | 976 B | | ProjectItemInstance_DefiningProjectDirectory_Repeated | 9.092 μs | 0.0654 μs | 0.0580 μs | 0.4883 | 8304 B | | ProjectItemInstance_DefiningProjectNameExtension_AllItems_SingleProject | 12.515 μs | 0.0297 μs | 0.0263 μs | 0.5188 | 8864 B | | ProjectItemInstance_DefiningProjectFullPath_AllItems_MultiProject | 35.245 μs | 0.0405 μs | 0.0379 μs | - | 64 B | | ProjectItemInstance_DefiningProjectDirectory_AllItems_MultiProject_Repeated | 878.931 μs | 3.1495 μs | 2.7919 μs | 48.8281 | 824640 B | | TaskItem_AllDefiningProjectModifiers_Once | 1.430 μs | 0.0027 μs | 0.0023 μs | 0.0534 | 912 B | | TaskItem_DefiningProjectNameExtension_AllItems | 11.628 μs | 0.0289 μs | 0.0241 μs | 0.5188 | 8800 B | | TaskItem_DefiningProjectDirectory_Repeated | 9.121 μs | 0.0192 μs | 0.0170 μs | 0.4883 | 8240 B | ### .NET Framework 4.8.1 | Method | Mean | Error | StdDev | Gen0 | Allocated | |--------------------------------------------- |-------------:|-----------:|----------:|-------:|----------:| | IsItemSpecModifier_AllModifiers | 227.45 ns | 0.297 ns | 0.264 ns | - | - | | IsDerivableItemSpecModifier_RecursiveDir | 10.49 ns | 0.019 ns | 0.018 ns | - | - | | GetItemSpecModifier_FullPath | 1,267.45 ns | 0.844 ns | 0.705 ns | - | - | | GetItemSpecModifier_Directory | 2,433.43 ns | 7.400 ns | 6.560 ns | 0.0954 | 517 B | | GetItemSpecModifier_ModifiedTime | 41,881.06 ns | 111.954 ns | 93.487 ns | 0.1221 | 878 B | | GetItemSpecModifier_DefiningProjectDirectory | 5,467.24 ns | 8.295 ns | 6.926 ns | 0.1602 | 857 B | | TaskItem_AllDerivableModifiers_Once | 5.322 μs | 0.0354 μs | 0.0331 μs | 0.3662 | 1923 B | | TaskItem_FilenameAndExtension_Repeated | 10.238 μs | 0.0108 μs | 0.0101 μs | 0.1373 | 761 B | | TaskItem_Filename_ManyItems | 110.078 μs | 0.1367 μs | 0.1212 μs | 2.3193 | 12379 B | | TaskItem_FullPathDerivedModifiers_Repeated | 26.619 μs | 0.1695 μs | 0.1585 μs | 2.1973 | 11578 B | | ProjectItemInstance_AllDerivableModifiers_Once | 5.525 μs | 0.0311 μs | 0.0276 μs | 0.3662 | 1959 B | | ProjectItemInstance_FilenameExtension_AllItems | 216.406 μs | 0.2767 μs | 0.2160 μs | 2.9297 | 16422 B | | ProjectItemInstance_FilenameExtension_AllItems_Repeated | 2,161.623 μs | 10.4073 μs | 9.2258 μs | 31.2500 | 164225 B | | ProjectItemInstance_AllDefiningProjectModifiers_Once | 9.808 μs | 0.0177 μs | 0.0165 μs | 0.2594 | 1418 B | | ProjectItemInstance_DefiningProjectDirectory_Repeated | 64.204 μs | 0.3983 μs | 0.3726 μs | 2.3193 | 12616 B | | ProjectItemInstance_DefiningProjectNameExtension_AllItems_SingleProject | 131.504 μs | 0.3383 μs | 0.3165 μs | 2.1973 | 12456 B | | ProjectItemInstance_DefiningProjectFullPath_AllItems_MultiProject | 173.171 μs | 0.7242 μs | 0.5654 μs | - | 38 B | | ProjectItemInstance_DefiningProjectDirectory_AllItems_MultiProject_Repeated | 6,399.063 μs | 71.6927 μs | 67.0614 μs | 234.3750 | 1242225 B | | TaskItem_AllDefiningProjectModifiers_Once | 8.636 μs | 0.0177 μs | 0.0147 μs | 0.2594 | 1382 B | | TaskItem_DefiningProjectNameExtension_AllItems | 126.982 μs | 0.2250 μs | 0.2104 μs | 2.1973 | 12420 B | | TaskItem_DefiningProjectDirectory_Repeated | 58.025 μs | 0.1388 μs | 0.1299 μs | 2.3804 | 12579 B | ## Final Benchmark Results ### .NET 10.0 | Method | Mean | Error | StdDev | Gen0 | Allocated | |--------------------------------------------- |--------------:|------------:|------------:|-------:|----------:| | IsItemSpecModifier_AllModifiers | 38.4923 ns | 0.5501 ns | 0.5145 ns | - | - | | IsDerivableItemSpecModifier_RecursiveDir | 0.0000 ns | 0.0000 ns | 0.0000 ns | - | - | | GetItemSpecModifier_FullPath | 260.1019 ns | 0.3623 ns | 0.3212 ns | - | - | | GetItemSpecModifier_Directory | 413.2121 ns | 1.9441 ns | 1.8185 ns | 0.0224 | 376 B | | GetItemSpecModifier_ModifiedTime | 30,272.3116 ns | 408.0450 ns | 381.6855 ns | - | 176 B | | GetItemSpecModifier_DefiningProjectDirectory | 72.3691 ns | 0.0289 ns | 0.0270 ns | - | - | | TaskItem_AllDerivableModifiers_Once | 87.41 ns | 0.226 ns | 0.200 ns | - | - | | TaskItem_FilenameAndExtension_Repeated | 173.68 ns | 1.812 ns | 1.695 ns | - | - | | TaskItem_Filename_ManyItems | 1,856.79 ns | 1.128 ns | 0.942 ns | - | - | | TaskItem_FullPathDerivedModifiers_Repeated | 321.57 ns | 0.301 ns | 0.267 ns | - | - | | ProjectItemInstance_AllDerivableModifiers_Once | 143.78 ns | 0.493 ns | 0.462 ns | 0.0038 | 64 B | | ProjectItemInstance_FilenameExtension_AllItems | 5,343.30 ns | 4.955 ns | 4.138 ns | - | 64 B | | ProjectItemInstance_FilenameExtension_AllItems_Repeated | 67,028.30 ns | 252.985 ns | 224.264 ns | - | 640 B | | ProjectItemInstance_AllDefiningProjectModifiers_Once | 459.4 ns | 0.34 ns | 0.31 ns | 0.0038 | 64 B | | ProjectItemInstance_DefiningProjectDirectory_Repeated | 1,020.5 ns | 0.56 ns | 0.47 ns | 0.0038 | 64 B | | ProjectItemInstance_DefiningProjectNameExtension_AllItems_SingleProject | 20,091.0 ns | 17.96 ns | 16.80 ns | - | 64 B | | ProjectItemInstance_DefiningProjectFullPath_AllItems_MultiProject | 9,946.7 ns | 5.28 ns | 4.68 ns | - | 64 B | | ProjectItemInstance_DefiningProjectDirectory_AllItems_MultiProject_Repeated | 103,960.5 ns | 57.74 ns | 48.21 ns | - | 640 B | | TaskItem_AllDefiningProjectModifiers_Once | 400.5 ns | 0.15 ns | 0.12 ns | - | - | | TaskItem_DefiningProjectNameExtension_AllItems | 19,150.3 ns | 11.55 ns | 10.24 ns | - | - | | TaskItem_DefiningProjectDirectory_Repeated | 970.8 ns | 0.62 ns | 0.55 ns | - | - | ### .NET Framework 4.8.1 | Method | Mean | Error | StdDev | Gen0 | Allocated | |--------------------------------------------- |-------------:|-----------:|----------:|-------:|----------:| | IsItemSpecModifier_AllModifiers | 106.532 ns | 0.4289 ns | 0.4012 ns | - | - | | IsDerivableItemSpecModifier_RecursiveDir | 5.067 ns | 0.0031 ns | 0.0024 ns | - | - | | GetItemSpecModifier_FullPath | 1,331.138 ns | 1.4666 ns | 1.3001 ns | - | - | | GetItemSpecModifier_Directory | 2,471.887 ns | 4.2333 ns | 3.7527 ns | 0.0954 | 517 B | | GetItemSpecModifier_ModifiedTime | 44,773.794 ns | 405.2647 ns | 359.2566 ns | 0.1221 | 878 B | | GetItemSpecModifier_DefiningProjectDirectory | 156.840 ns | 0.2242 ns | 0.1987 ns | - | - | | TaskItem_AllDerivableModifiers_Once | 507.2 ns | 1.19 ns | 1.11 ns | - | - | | TaskItem_FilenameAndExtension_Repeated | 664.7 ns | 0.39 ns | 0.35 ns | - | - | | TaskItem_Filename_ManyItems | 6,916.2 ns | 4.03 ns | 3.77 ns | - | - | | TaskItem_FullPathDerivedModifiers_Repeated | 2,261.6 ns | 3.34 ns | 2.96 ns | - | - | | ProjectItemInstance_AllDerivableModifiers_Once | 688.6 ns | 1.01 ns | 0.94 ns | 0.0067 | 36 B | | ProjectItemInstance_FilenameExtension_AllItems | 20,366.3 ns | 11.28 ns | 10.56 ns | - | 36 B | | ProjectItemInstance_FilenameExtension_AllItems_Repeated | 202,570.4 ns | 271.12 ns | 240.34 ns | - | 362 B | | ProjectItemInstance_AllDefiningProjectModifiers_Once | 1,153.5 ns | 13.83 ns | 12.26 ns | 0.0057 | 36 B | | ProjectItemInstance_DefiningProjectDirectory_Repeated | 2,795.7 ns | 17.33 ns | 16.21 ns | 0.0038 | 36 B | | ProjectItemInstance_DefiningProjectNameExtension_AllItems_SingleProject | 46,236.9 ns | 105.16 ns | 93.22 ns | - | 36 B | | ProjectItemInstance_DefiningProjectFullPath_AllItems_MultiProject | 28,362.1 ns | 34.50 ns | 30.58 ns | - | 36 B | | ProjectItemInstance_DefiningProjectDirectory_AllItems_MultiProject_Repeated | 282,316.9 ns | 1,944.00 ns | 1,818.42 ns | - | 364 B | | TaskItem_AllDefiningProjectModifiers_Once | 965.3 ns | 5.43 ns | 4.53 ns | - | - | | TaskItem_DefiningProjectNameExtension_AllItems | 42,707.8 ns | 179.84 ns | 168.23 ns | - | - | | TaskItem_DefiningProjectDirectory_Repeated | 2,539.1 ns | 9.64 ns | 8.05 ns | - | - |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #(not specified - documentation task)
Context
The
multithreaded-prototype-rebasedfeature branch contains substantial implementation work beyond the foundational APIs present in main. This spec documents the gap to enable systematic integration and implements the File I/O task migration as a first step.Changes Made
Added
documentation/specs/multithreading/threading-fixes-and-task-migration-status.mddocumenting:Threading Fixes (11 core infrastructure changes):
NativeMethods.CurrentThreadWorkingDirectory,Modifiers.cs,TaskBuilder.cs)Wave18_0gating)Tasks Migration Section:
IMultiThreadableTaskinterface documented with implementation detailsImplementation Guidance:
File I/O Task Migration Implementation:
FileState.csto acceptTaskEnvironmentparameter for thread-safe path resolution[MSBuildMultiThreadableTask]attribute andIMultiThreadableTaskinterface, usesTaskEnvironment.GetAbsolutePath()for path resolution andTaskEnvironment.GetEnvironmentVariable()for environment accessMSBuildMultiThreadableTaskAttributefor routing andIMultiThreadableTaskinterface for TaskEnvironment accessTesting
Notes
Main branch has APIs (
IMultiThreadableTask,TaskEnvironment,TaskRouter) but lacks functional implementation. This PR provides both documentation roadmap for merging ~256 commits of threading work from feature branch and implements the first wave of task migrations (7 File I/O tasks) to demonstrate the migration pattern.Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.