perf: batch individual file inputs for parallel formatting#1818
Open
ansemb wants to merge 1 commit intobelav:mainfrom
Open
perf: batch individual file inputs for parallel formatting#1818ansemb wants to merge 1 commit intobelav:mainfrom
ansemb wants to merge 1 commit intobelav:mainfrom
Conversation
When multiple individual file paths are passed to CSharpier (e.g. from pre-commit hooks), each file was previously processed sequentially with its own OptionsProvider and FormattingCache. This caused severe performance degradation (~15x slower than directory input). Refactored FormatPhysicalFiles to collect individual file paths, compute their common ancestor directory, and create a single shared OptionsProvider and FormattingCache. Files are then formatted in parallel using Task.WhenAll, matching the directory input code path. Added tests for multi-file formatting, per-directory config resolution, mixed file/directory input, and the GetCommonAncestor helper method.
ansemb
commented
Feb 17, 2026
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.
Problem
When passing 200+ individual file paths to CSharpier (e.g. with pre-commit hooks), formatting is significantly slower than passing a directory containing the same files.
Directory:
Files:
Before (293 files, individual paths): Formatted 293 files in 40886ms.
After (293 files, individual paths): Formatted 293 files in 859ms.
Root Cause
In
CommandLineFormatter.FormatPhysicalFiles, each individual file path was processed inside the input loop with:OptionsProviderper file (expensive — reads.csharpierrc,.editorconfig,.gitignorefrom disk and walks up directories)FormattingCacheper file (reads/writes a separate cache JSON each time)In contrast, directory inputs already used a single
OptionsProvider, a singleFormattingCache, and parallel formatting viaTask.WhenAll.Solution
Individual file paths are now collected into a pending list during the input loop. After all inputs are processed:
GetCommonAncestorhelperOptionsProvideris created, pre-seeded at the common ancestor. Config resolution still walks up from each file's own directory via its internalConcurrentDictionarycaches, so per-directory configs (e.g. different.csharpierrcin different subdirectories) are respected correctly.FormattingCacheis shared across all filesTask.WhenAll, matching the directory code pathDirectory inputs continue to work exactly as before.
Tests Added
Multiple_Files_Should_Be_Formatted— verifies files from different subdirectories are formattedMultiple_Files_Should_Respect_Per_Directory_Config— verifies per-directory.csharpierrcis respected when using a sharedOptionsProviderMixed_Files_And_Directory_Should_Format_All— verifies mixed directory + individual file inputsGetCommonAncestor_Returns_Common_Path— 5 parameterized cases covering same dir, nested, divergent, root-only, and empty-string fallback