Last active
February 26, 2026 06:02
-
-
Save RikkiGibson/3c61e2f3ba6700268966778f4394745b to your computer and use it in GitHub Desktop.
Directory.EnumerateFiles perf
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
| using System.Runtime.InteropServices; | |
| using BenchmarkDotNet.Attributes; | |
| using BenchmarkDotNet.Running; | |
| BenchmarkRunner.Run<FindFirstFileBenchmark>(); | |
| [MemoryDiagnoser] | |
| public partial class FindFirstFileBenchmark | |
| { | |
| [Params(@"F:\src\roslyn")] | |
| public string BasePath { get; set; } = ""; | |
| [Params(@"F:\src\roslyn\artifacts\bin\Microsoft.Build.Tasks.CodeAnalysis\Debug\net10.0\cs\")] | |
| public string SearchPath { get; set; } = ""; | |
| // ───────────────────────────── Managed ───────────────────────────── | |
| [Benchmark(Baseline = true)] | |
| public bool DirectoryEnumerateFiles() | |
| { | |
| var searchPath = SearchPath; | |
| while (searchPath != BasePath && searchPath is { }) | |
| { | |
| if (Directory.EnumerateFiles(searchPath, "*.docx").Any()) | |
| return true; | |
| searchPath = Path.GetDirectoryName(searchPath); | |
| } | |
| return false; | |
| } | |
| // ──────────────────────────── Native ──────────────────────────── | |
| [Benchmark] | |
| public unsafe bool FindFirstFileW() | |
| { | |
| var searchPath = SearchPath; | |
| while (searchPath != BasePath && searchPath is { }) | |
| { | |
| if (find(searchPath)) | |
| return true; | |
| searchPath = Path.GetDirectoryName(searchPath); | |
| } | |
| return false; | |
| bool find(string searchPath) | |
| { | |
| string searchPattern = Path.Combine(searchPath, "*.docx"); | |
| WIN32_FIND_DATAW findData; | |
| var handle = NativeMethods.FindFirstFileW(searchPattern, &findData); | |
| if (handle == new nint(-1)) | |
| return false; | |
| NativeMethods.FindClose(handle); | |
| return true; | |
| } | |
| } | |
| } | |
| // ═══════════════════════════════ P/Invoke ═══════════════════════════════ | |
| internal static partial class NativeMethods | |
| { | |
| [LibraryImport("kernel32.dll", EntryPoint = "FindFirstFileW", StringMarshalling = StringMarshalling.Utf16)] | |
| internal static unsafe partial nint FindFirstFileW(string lpFileName, WIN32_FIND_DATAW* lpFindFileData); | |
| [LibraryImport("kernel32.dll", EntryPoint = "FindNextFileW", StringMarshalling = StringMarshalling.Utf16)] | |
| [return: MarshalAs(UnmanagedType.Bool)] | |
| internal static unsafe partial bool FindNextFileW(nint hFindFile, WIN32_FIND_DATAW* lpFindFileData); | |
| [LibraryImport("kernel32.dll", EntryPoint = "FindClose")] | |
| [return: MarshalAs(UnmanagedType.Bool)] | |
| internal static partial bool FindClose(nint hFindFile); | |
| } | |
| [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] | |
| internal unsafe struct WIN32_FIND_DATAW | |
| { | |
| public FileAttributes dwFileAttributes; | |
| public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; | |
| public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; | |
| public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; | |
| public uint nFileSizeHigh; | |
| public uint nFileSizeLow; | |
| public uint dwReserved0; | |
| public uint dwReserved1; | |
| public fixed char cFileName[260]; | |
| public fixed char cAlternateFileName[14]; | |
| } |
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
| // * Summary * | |
| BenchmarkDotNet v0.14.0, Windows 11 (10.0.26200.7840) | |
| AMD Ryzen 9 3900X, 1 CPU, 24 logical and 12 physical cores | |
| .NET SDK 10.0.103 | |
| [Host] : .NET 10.0.3 (10.0.326.7603), X64 RyuJIT AVX2 | |
| DefaultJob : .NET 10.0.3 (10.0.326.7603), X64 RyuJIT AVX2 | |
| | Method | BasePath | SearchPath | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | | |
| |------------------------ |---------------- |------------------------ |---------:|--------:|---------:|------:|--------:|-------:|----------:|------------:| | |
| | DirectoryEnumerateFiles | F:\\src\\roslyn | F:\\sr(...)0\\cs\\ [80] | 227.7 us | 2.02 us | 1.89 us | 1.00 | 0.01 | 0.2441 | 3.38 KB | 1.00 | | |
| | FindFirstFileW | F:\\src\\roslyn | F:\\sr(...)0\\cs\\ [80] | 383.3 us | 7.60 us | 11.37 us | 1.68 | 0.05 | - | 1.94 KB | 0.57 | | |
| // * Legends * | |
| BasePath : Value of the 'BasePath' parameter | |
| SearchPath : Value of the 'SearchPath' parameter | |
| Mean : Arithmetic mean of all measurements | |
| Error : Half of 99.9% confidence interval | |
| StdDev : Standard deviation of all measurements | |
| Ratio : Mean of the ratio distribution ([Current]/[Baseline]) | |
| RatioSD : Standard deviation of the ratio distribution ([Current]/[Baseline]) | |
| Gen0 : GC Generation 0 collects per 1000 operations | |
| Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B) | |
| Alloc Ratio : Allocated memory ratio distribution ([Current]/[Baseline]) | |
| 1 us : 1 Microsecond (0.000001 sec) | |
| // * Diagnostic Output - MemoryDiagnoser * | |
| // ***** BenchmarkRunner: End ***** | |
| Run time: 00:00:56 (56.9 sec), executed benchmarks: 2 | |
| Global total time: 00:01:03 (63.12 sec), executed benchmarks: 2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment