获得多个FileInfo的更快捷方式?

时间:2010-12-04 09:13:33

标签: c# file-io winapi

这是一个远景,但有更快的方法来获取多个文件的大小,lastaccessedtime,lastcreated time等?

我有很长的文件路径列表(所以我不需要枚举)并且需要尽快查找该信息。并行创建FileInfo可能无济于事,因为瓶颈应该是磁盘。

NTFS日记本只保留文件名,不过很好,我觉得操作系统不会在某处保存那个元信息?

如果存在静态或Win32调用(文件方法只允许我一次获取一条信息),可以执行的另一个优化方法是获取信息而不是创建一堆FileInfo对象

无论如何,很高兴,如果有人知道可能会有所帮助的事情,不幸的是我必须在这里进行微优化并且没有“使用数据库”不是一个可行的答案;)

5 个答案:

答案 0 :(得分:9)

System.IO.File上有静态方法可以获得你想要的东西。这是一项微观优化,但它可能就是您所需要的:GetLastAccessTimeGetCreationTime

修改

我会留下上面的文字,因为你特意要求静态方法。但是,我认为你最好使用FileInfo(你应该测量以确定)。 File和FileInfo都使用名为File的{​​{1}}上的内部方法来获取您所追踪的数据。对于您需要的属性,FillAttributeInfo将需要调用此方法一次。 FileInfo必须在每次调用时调用它,因为属性info对象在方法完成时被抛弃(因为它是静态的)。

所以我的预感是,当你需要多个属性时,每个文件的File会更快。但在性能情况下,你应该总是测量!面对这个问题,我会尝试上面列出的两个托管选项,并在串行和并行运行时进行基准测试。然后决定它是否足够快。

如果速度不够快,您需要直接调用Win32 API。在参考资料中查看FileInfo并提出类似的东西并不难。

第二次编辑

事实上,如果你真的需要它,这是使用与File.FileAttributeInfo的内部代码相同的方法直接调用Win32 API所需的代码,但使用一个OS调用来获取所有属性。我认为你应该只在必要时使用它。您必须自己从FILETIME解析为可用的日期时间等,因此您需要手动完成更多工作。

File

答案 1 :(得分:4)

.NET的DirectoryInfo和FileInfo类在这个问题上非常慢,特别是在与网络共享一起使用时。

如果要“扫描”的许多文件位于同一目录中,那么通过使用Win32 API的FindFirstFile,FindNextFile和FindClose函数,您将获得更快的结果(取决于具体情况:尺寸更快)。如果您需要提供实际需要的更多信息(例如,如果您要求目录中的所有“.log”文件,您只需要75%的文件),情况就是如此。

实际上,.NET的info类也在内部使用这些Win32 API函数。但他们只是“记住”文件名。在询问有关一堆文件(例如LastModified)的更多信息时,会为每个文件提出单独的(网络)请求,这会占用时间。

答案 2 :(得分:2)

是否可以使用DirectoryInfo类?

 DirectoryInfo d = new DirectoryInfo(@"c:\\Temp");
 FileInfo[] f= d.GetFiles()

答案 3 :(得分:0)

我认为您正在寻找GetFileAttributesEx函数(pinvoke.net link)。但是,FileInfo类(或更确切地说,它的基类)无论如何都在内部使用它,所以我怀疑你会看到任何性能改进。

答案 4 :(得分:0)

如果文件系统是远程的,则并行性可能会有所帮助,因为网络可能是瓶颈。

此测试案例显示使用8个线程的50k文件的性能提高了约5倍(52s => 11s)。避免lock()也是至关重要的,因为调用50k会产生很大的影响。计时是在不运行调试器的情况下完成的。

这也说明在访问FileInfo.Length之前不会执行获取文件长度的工作。并行部分瞬时完成后,再次访问“长度”。这可能有点依赖于实现。

// ~4s
//
List<string> files = Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.AllDirectories)
    .ToList();

// ~0s
// 
Dictionary<string, FileInfo> fileMap = files.Select(file => new
{
    file,
    info = new FileInfo(file)
})
.ToDictionary(f => f.file, f => f.info);

// ~10s
//
Int64 totalSize = fileMap.Where(kv => kv.Value != null)
    .AsParallel() // ~50s w/o this 
    .Select(kv =>
    {
        try
        {
            return kv.Value.Length;
        }
        catch (FileNotFoundException)  // a transient file or directory
        {
        }
        catch (UnauthorizedAccessException)
        {
        }
        return 0;
    })
    .Sum();