From: Adam Sitnik Date: Fri, 19 Nov 2021 12:02:26 +0000 (+0100) Subject: Reduce allocations for CreateDirectory (#61777) X-Git-Tag: accepted/tizen/unified/riscv/20231226.055536~12170 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ba4eae0b96f2a569050bd315b0b51609993e237c;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Reduce allocations for CreateDirectory (#61777) * introduce an overload that accepts ROS instead of a string * remove dead code * avoid string allocations * remove List allocation * Apply suggestions from code review Co-authored-by: Stephen Toub --- diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MkDir.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MkDir.cs index 499b1e7..09a4ae1 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MkDir.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MkDir.cs @@ -3,12 +3,20 @@ using System; using System.Runtime.InteropServices; +using System.Text; internal static partial class Interop { internal static partial class Sys { - [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MkDir", CharSet = CharSet.Ansi, SetLastError = true)] - internal static partial int MkDir(string path, int mode); + [GeneratedDllImport(Libraries.SystemNative, EntryPoint = "SystemNative_MkDir", SetLastError = true)] + private static partial int MkDir(ref byte path, int mode); + + internal static int MkDir(ReadOnlySpan path, int mode) + { + using ValueUtf8Converter converter = new(stackalloc byte[DefaultPathBufferSize]); + int result = MkDir(ref MemoryMarshal.GetReference(converter.ConvertAndTerminateString(path)), mode); + return result; + } } } diff --git a/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj b/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj index ae48db6..d597398 100644 --- a/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj +++ b/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj @@ -137,8 +137,6 @@ Link="Common\Interop\Unix\Interop.GetHostName.cs" /> - - + diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs index 2fa979b..f4f0cac 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs @@ -434,23 +434,6 @@ namespace System.IO.Pipes return LazyInitializer.EnsureInitialized(ref _asyncActiveSemaphore, () => new SemaphoreSlim(1, 1)); } - private static void CreateDirectory(string directoryPath) - { - int result = Interop.Sys.MkDir(directoryPath, (int)Interop.Sys.Permissions.Mask); - - // If successful created, we're done. - if (result >= 0) - return; - - // If the directory already exists, consider it a success. - Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); - if (errorInfo.Error == Interop.Error.EEXIST) - return; - - // Otherwise, fail. - throw Interop.GetExceptionForIoErrno(errorInfo, directoryPath, isDirectory: true); - } - /// Creates an anonymous pipe. /// The resulting reader end of the pipe. /// The resulting writer end of the pipe. diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs index e23ae9e..e5fd857 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs @@ -298,9 +298,8 @@ namespace System.IO { return; // Path already exists and it's a directory. } - else if (errorInfo.Error == Interop.Error.ENOENT) + else if (errorInfo.Error == Interop.Error.ENOENT) // Some parts of the path don't exist yet. { - // Some parts of the path don't exist yet. CreateParentsAndDirectory(fullPath); } else @@ -309,20 +308,19 @@ namespace System.IO } } - public static void CreateParentsAndDirectory(string fullPath) + private static void CreateParentsAndDirectory(string fullPath) { // Try create parents bottom to top and track those that could not // be created due to missing parents. Then create them top to bottom. - List stackDir = new List(); - - stackDir.Add(fullPath); + using ValueListBuilder stackDir = new(stackalloc int[32]); // 32 arbitrarily chosen + stackDir.Append(fullPath.Length); int i = fullPath.Length - 1; - // Trim trailing separator. if (PathInternal.IsDirectorySeparator(fullPath[i])) { - i--; + i--; // Trim trailing separator. } + do { // Find the end of the parent directory. @@ -332,13 +330,11 @@ namespace System.IO i--; } - // Try create it. - string mkdirPath = fullPath.Substring(0, i); + ReadOnlySpan mkdirPath = fullPath.AsSpan(0, i); int result = Interop.Sys.MkDir(mkdirPath, (int)Interop.Sys.Permissions.Mask); if (result == 0) { - // Created parent. - break; + break; // Created parent. } Interop.ErrorInfo errorInfo = Interop.Sys.GetLastErrorInfo(); @@ -348,7 +344,7 @@ namespace System.IO // We'll try to create its parent on the next iteration. // Track this path for later creation. - stackDir.Add(mkdirPath); + stackDir.Append(mkdirPath.Length); } else if (errorInfo.Error == Interop.Error.EEXIST) { @@ -358,15 +354,15 @@ namespace System.IO } else { - throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath, isDirectory: true); + throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath.ToString(), isDirectory: true); } i--; } while (i > 0); // Create directories that had missing parents. - for (i = stackDir.Count - 1; i >= 0; i--) + for (i = stackDir.Length - 1; i >= 0; i--) { - string mkdirPath = stackDir[i]; + ReadOnlySpan mkdirPath = fullPath.AsSpan(0, stackDir[i]); int result = Interop.Sys.MkDir(mkdirPath, (int)Interop.Sys.Permissions.Mask); if (result < 0) { @@ -386,7 +382,7 @@ namespace System.IO } } - throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath, isDirectory: true); + throw Interop.GetExceptionForIoErrno(errorInfo, mkdirPath.ToString(), isDirectory: true); } } }