Skip to content

Tracking FileSystem provider performance issues #14497

@iSazonov

Description

@iSazonov
  1. The IsPathRoot() method does double check for UNC
    /// <summary>
    /// Determines if the specified path is either a drive root or a UNC root.
    /// </summary>
    /// <param name="path">
    /// The path
    /// </param>
    /// <returns>
    /// True if the path is either a drive root or a UNC root, or false otherwise.
    /// </returns>
    private static bool IsPathRoot(string path)
    {
    if (string.IsNullOrEmpty(path))
    {
    return false;
    }
    bool isDriveRoot = string.Equals(path, Path.GetPathRoot(path), StringComparison.OrdinalIgnoreCase);
    bool isUNCRoot = IsUNCRoot(path);
    bool result = isDriveRoot || isUNCRoot;
    s_tracer.WriteLine("result = {0}; isDriveRoot = {1}; isUNCRoot = {2}", result, isDriveRoot, isUNCRoot);
    return result;
    }

    and could be replaced with more short and fast code. Proposal from @mklement0 Tracking FileSystem provider performance issues #14497 (comment)

Also we need to review GetFileSystemItem(string path, ref bool isContainer, bool showHidden) - there is a root check and maybe a bug (no UNC share check)

bool isRootPath =
string.Equals(
Path.GetPathRoot(path),
result.FullName,
StringComparison.OrdinalIgnoreCase);

  1. PR Reduce aggressive correcting casing of file system paths #14469 Reduce aggressive correcting casing of file system paths
    On Windows we do case normalizations even if results are not used.

  2. No need to allocate FileInfo and extra strings in IsSameWindowsVolume():

    private static bool IsSameWindowsVolume(string source, string destination)
    {
    FileInfo src = new FileInfo(source);
    FileInfo dest = new FileInfo(destination);
    return (src.Directory.Root.Name == dest.Directory.Root.Name);
    }

    We can use GetDirectoryName(ReadOnlySpan<char> path). Also we should use Ordinal comparison.

  3. EnsureDriveIsRooted() method does extra path.IndexOf(':') - we can simply check last char

    private static string EnsureDriveIsRooted(string path)
    {
    string result = path;
    // Find the drive separator
    int index = path.IndexOf(':');
    if (index != -1)
    {
    // if the drive separator is the end of the path, add
    // the root path separator back
    if (index + 1 == path.Length)
    {
    result = path + StringLiterals.DefaultPathSeparator;
    }
    }
    return result;
    }

  4. We need to review semantic of IsValidPath() method. If it is exactly about path validity the method could be simplified but we need to measure a performance. In common case it is impossible to validate path by parsing but only by opening.

    protected override bool IsValidPath(string path)
    {
    // Path passed should be fully qualified path.
    if (string.IsNullOrEmpty(path))
    {
    return false;
    }
    // Normalize the path
    path = NormalizePath(path);
    path = EnsureDriveIsRooted(path);
    #if !UNIX
    // Remove alternate data stream references
    // See if they've used the inline stream syntax. They have more than one colon.
    int firstColon = path.IndexOf(':');
    int secondColon = path.IndexOf(':', firstColon + 1);
    if (secondColon > 0)
    {
    path = path.Substring(0, secondColon);
    }
    #endif
    // Make sure the path is either drive rooted or UNC Path
    if (!IsAbsolutePath(path) && !Utils.PathIsUnc(path))
    {
    return false;
    }
    // Exceptions should only deal with exceptional circumstances,
    // but unfortunately, FileInfo offers no Try() methods that
    // let us check if we _could_ open the file.
    try
    {
    FileInfo testFile = new FileInfo(path);
    }
    catch (Exception e)
    {
    if ((e is ArgumentNullException) ||
    (e is ArgumentException) ||
    (e is System.Security.SecurityException) ||
    (e is UnauthorizedAccessException) ||
    (e is PathTooLongException) ||
    (e is NotSupportedException))
    {
    return false;
    }
    else
    {
    throw;
    }
    }
    return true;
    }

  5. Statistic shows:

dir -Recurse 'C:\Program Files (x86)\' | Select -ExpandProperty Mode | Sort-Object  | Group-Object -NoElement

Count Name
----- ----
    1 -----
    3 --r--
44263 -a---
    6 -ar--
 8136 d----
    3 d---s
    2 d-r--
 1044 la---

and we could add "la---" as a static element in

if (!isLink)
{
// special casing for the common cases - no allocations
switch (fileAttributes)
{
case FileAttributes.Archive:
return "-a---";
case FileAttributes.Directory:
return "d----";
case FileAttributes.Normal:
return "-----";
case FileAttributes.Directory | FileAttributes.ReadOnly:
return "d-r--";
case FileAttributes.Archive | FileAttributes.ReadOnly:
return "-ar--";
}
}

                if (!isLink)
                {
                    // special casing for the common cases - no allocations
                    switch (fileAttributes)
                    {
                        case FileAttributes.Archive:
                            return "-a---";
                        case FileAttributes.Directory:
                            return "d----";
                        case FileAttributes.Normal:
                            return "-----";
                        case FileAttributes.Directory | FileAttributes.ReadOnly:
                            return "d-r--";
                        case FileAttributes.Archive | FileAttributes.ReadOnly:
                            return "-ar--";
                    }
                }
                else if (fileAttributes == FileAttributes.ReadOnly)
                {
                    return "la---";
                }

           ...
  1. Remove double check ItemExists();
    private void CopyAndDelete(DirectoryInfo directory, string destination, bool force)
    {
    if (!ItemExists(destination))
    {
    CreateDirectory(destination, false);
    }
    else if (ItemExists(destination) && !IsItemContainer(destination))
    {

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Enhancementthe issue is more of a feature request than a bugWG-Engine-Performancecore PowerShell engine, interpreter, and runtime performance

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions