【问题标题】:Why is File.Exists() much slower when the file does not exist?当文件不存在时,为什么 File.Exists() 慢得多?
【发布时间】:2010-02-08 22:36:33
【问题描述】:

在我看来,当文件不存在或用户无权访问时,File.Exists() 比文件存在时慢得多。

这是真的吗?

这对我来说没有意义。

【问题讨论】:

    标签: c# .net file


    【解决方案1】:

    File.Exists 正在捕获异常。引发和捕获异常的开销可能会导致性能下降。

    File.Exists 是这样工作的:

    要检查文件是否存在,它会尝试打开文件...如果抛出异常,则文件不存在。

    该过程比打开文件慢,并且没有抛出异常(即文件存在时)。

    【讨论】:

    • 谢谢。我在文档中注意到了这一点并想知道。它可能也会影响性能。我会为你点击向上箭头,但我似乎还没有声誉。
    • 这也是我的第一个想法。 @Sarah,如果确实有问题,您可能希望退回到 P/Invoking Win32 GetFileAttributes() (如果未找到文件,则返回 -1)。
    • 那是 File.Exists 试图打开文件的问题。如果另一个线程同时尝试访问该文件(在打开文件的同时),我们只能说会有不幸的后果!
    【解决方案2】:

    File.Exists 还在检查文件是否存在之前实例化 CLR 权限。如果您要进行大量检查,另一种选择(尽管我没有尝试过性能)是PathFileExists

    [DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private extern static bool PathFileExists(StringBuilder path);
    
    void Exists()
    {
        // A StringBuilder is required for interops calls that use strings
        StringBuilder builder = new StringBuilder();
        builder.Append(@"C:\test.txt");
        bool exists = PathFileExists(builder);
    }
    

    【讨论】:

    • 这要快得多。我刚刚对使用 File.Exists 的一些代码进行了非隔离性能测试。然后我在更新我的实现以使用上面的方法后重新运行它。原始代码(在相同的使用场景中)占用了 5% 的堆栈跟踪时间。使用这种方法,这个数字下降到大约 0.5%。对于我们反复检查文件是否存在的过程来说,这是一个巨大的改进。谢谢!
    • 速度之快令人震惊!
    • 我正在做数以千计的 File.Exists 检查,这花了很多时间。此解决方案将处理时间缩短到一小部分。
    • 我已经测试过了,这和 File.Exists 似乎没有任何区别。
    • 我也没有看到任何速度差异
    【解决方案3】:

    通常,当您在一堆东西中搜索某样东西时,您无法确定它们不存在,除非您搜索了所有可能的地方。在搜索某物时(在大多数类型的集合中),最坏的情况是该项目在集合中不存在。

    我没有特别对 File.Exists 进行基准测试,但我非常怀疑这些情况下是否存在真正明显的差异,除非你这样做了数千次。您是如何得出这个结论的?

    【讨论】:

    • 确实——与List<T>.Contains比较。
    • 考虑一个存在但用户无权访问的文件。流程不会与用户拥有的流程相同吗?即:获取指向文件的指针并检查权限?或者权限可能会阻止查找文件来检查权限并且看起来像是一个空列表?或者在检查权限时会发生与您描述的相同的事情?我猜开始有点意义了。谢谢
    • 是的,权限是另一个要搜索的集合。如果你找到了你要找的东西,你可以立即放弃,而要断定你找不到你要找的东西,你必须检查所有的可能性。
    • 我得出了结论,因为我必须检查来自源列表的数千个文件的 File.Exists()。随着时间的推移,我注意到当更多文件丢失时,该过程需要更长的时间。这种差异在较小的环境中并不明显。
    • @Sarah 你在File.Exists 中使用了什么类型的路径?是绝对像C:\Windows\Notepad.exe 还是像.\My\File.txt
    【解决方案4】:

    我进行了以下测试,至少在我的 PC 上,时间大致相同:

      static void TestExists()
         {
         Stopwatch sw = Stopwatch.StartNew();
    
         for ( int i = 0; i < 1000; i++ )
            {
            if ( !File.Exists( @"c:\tmp\tmp" + i.ToString() + ".tmp" ) )
               Console.WriteLine( "File does not exist" );
            }
         Console.WriteLine( "Total for exists: " + sw.Elapsed );
    
         sw = Stopwatch.StartNew();
         for ( int i = 0; i < 1000; i++ )
            {
            if ( File.Exists( @"c:\tmp\tmp_" + i.ToString() + ".tmp" ) )
               Console.WriteLine( "File exists" );
            }
         Console.WriteLine( "Total for not exists: " + sw.Elapsed );
         }
    

    结果大致如下(每次运行略有不同但大致相同):

    Total for exists: 00:00:00.0717181
    Total for not exists: 00:00:00.0824266
    

    但是通过网络(在 LAN 上到一跳外的服务器),当文件实际存在时,我发现测试要慢很多。我闻了一下,每个方向只有一个 SMB 数据包。

    Total for exists: 00:00:02.4028708
    Total for not exists: 00:00:00.6910531
    

    【讨论】:

    • 这个测试不是很可靠,因为你也在测量控制台的输出。
    • @t3chb0t:显示的输出是总输出。我对此不太清楚,但是第一个循环使用现有文件运行(因此该循环没有输出),第二个循环用于不存在的文件(因此没有输出)。所以时间不包括任何控制台输出。如果包含 1000 行控制台输出,数字会大得多。
    【解决方案5】:

    File 及其所有方法通常适用于 Windows 文件句柄。

    如果你做了很多检查,你应该使用:

    FileInfo fiInfo = new FileInfo(@"c:\donotexists");
    if (fiInfo.Exists)
        return true;
    

    它不是在内部处理文件句柄,而是查看文件属性并且速度更快。此外,它不检查异常,这在 .NET 中是一个很大的减速

    【讨论】:

    • 看起来FileInfo.Exists 最终与File.Exists 发出相同的调用。 (使用Reflector可以看到它必须执行FillAttributeInfo并打开一个SafeFindHandle,就像File.Exists一样)。
    猜你喜欢
    • 2010-10-29
    • 2011-07-23
    • 2015-09-21
    • 1970-01-01
    • 2018-09-14
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    • 2014-11-07
    相关资源
    最近更新 更多