perf: use `FileSystemEnumerator`
Use FileSystemEnumerator to replace Directory.EnumerateFiles, this will only match valid CSharpier file types and skips large always ignored directories, like /bin or /.git
Note that this doesn't use IFileSystem.Directory.EnumerateFiles I've checked the implementation of DirectoryWrapper -> FileSystemEnumerableFactory.UserEntries which internally uses FileSystemEnumerable, so this is functionally the same but skips invalid files and ignored directories.
Unfortunately this causes an issue for the CLI.Tests as it uses MockFileSystem which doesn't really call FileSystemEnumerable causing the tests to fail. I added a fallback check for MockFileSystem but it feels extremely hacky and potentially error prone.
- Did wonder if it's worth implementing
IEnumerableinValidFilesEnumerator. - It might be worth parsing
gitignoreandcsharpierignorewhile parsing, it would slow things down due to reading files synchronously but it would avoid searching a lot of directories.- This would require a rework of how
ValidFilesEnumeratorsearches folders as we'd first have to check for ignore files before we can start yielding files as they may have been ignored.
- This would require a rework of how
Benchmarks
This change will have a varying impact on different projects. For instance my csharpier project has 20,000 files in it, most of which are probably in bin, git and benchmarks results. This will reduce the impact of #1743 and #1740
Before
| Method | Mean | Error | StdDev | Gen0 | Gen1 | Allocated |
|---|---|---|---|---|---|---|
| FormatCli | 949.7 ms | 18.81 ms | 35.32 ms | 9000.0000 | 1000.0000 | 81.49 MB |
After
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|---|---|---|---|---|---|
| FormatCli | 653.9 ms | 13.03 ms | 31.97 ms | 4000.0000 | 35.17 MB |