【问题标题】:Fast (lowlevel) method to recursively process files in folders递归处理文件夹中文件的快速(低级)方法
【发布时间】:2010-01-18 10:59:38
【问题描述】:

我的应用程序索引最终用户计算机上所有硬盘的内容。 我正在使用 Directory.GetFiles 和 Directory.GetDirectories 递归处理整个文件夹结构。我只索引了几个选定的文件类型(最多 10 个文件类型)。

我在分析器中看到,大部分索引时间都花在枚举文件和文件夹上 - 取决于实际索引时间高达 90% 的文件的比例。

我想让索引尽可能快。我已经优化了索引本身和索引文件的处理。

我曾考虑使用 Win32 API 调用,但实际上我在分析器中看到大部分处理时间实际上都花在了 .NET 完成的这些 API 调用上。

是否有可以从 C# 访问的(可能是低级别的)方法可以使文件/文件夹的枚举至少部分更快?


根据评论中的要求,我当前的代码(只是一个修剪了不相关部分的方案):

    private IEnumerable<IndexedEntity> RecurseFolder(string indexedFolder)
    {
        //for a single extension:
        string[] files = Directory.GetFiles(indexedFolder, extensionFilter);
        foreach (string file in files)
        {
            yield return ProcessFile(file);
        }
        foreach (string directory in Directory.GetDirectories(indexedFolder))
        {
            //recursively process all subdirectories
            foreach (var ie in RecurseFolder(directory))
            {
                yield return ie;
            }
        }
    }

【问题讨论】:

  • 您介意分享您现在拥有的代码吗?
  • @Bobby:我已经更新了问题
  • 性能方面,“API 深度”并不重要。最重要的是递归策略,在进入子文件夹之前读取/处理当前目录中的所有文件(Mrk Gravell 正确 - 假设 GetFiles() / GetDirectories 调用确实读取所有内容,并且 GetDirectories 调用是从文件系统提供的缓存)。
  • 这里提出了一种更快的方法,我会尝试:stackoverflow.com/questions/724148/… - 虽然仍然对其他选项感兴趣
  • 该实现 - 在跳过 .NET 包装器的同时 - 提前进入子目录,从而保留更多状态并可能破坏缓存。不过,我对 5-10 倍的说法感到惊讶 - 请在选择之前进行测量(并仔细测量......)

标签: c# .net windows optimization filesystems


【解决方案1】:

在.NET 4.0中,有内置的enumerable file listing methods;因为这不远,我会尝试使用它。如果您有大量填充的任何文件夹(需要大数组分配),这可能是一个特别因素。

如果 depth 是问题,我会考虑将您的方法展平以使用本地堆栈/队列和 single 迭代器块。这将减少用于枚举深层文件夹的代码路径:

    private static IEnumerable<string> WalkFiles(string path, string filter)
    {
        var pending = new Queue<string>();
        pending.Enqueue(path);
        string[] tmp;
        while (pending.Count > 0)
        {
            path = pending.Dequeue();
            tmp = Directory.GetFiles(path, filter);
            for(int i = 0 ; i < tmp.Length ; i++) {
                yield return tmp[i];
            }
            tmp = Directory.GetDirectories(path);
            for (int i = 0; i < tmp.Length; i++) {
                pending.Enqueue(tmp[i]);
            }
        }
    }

重复此操作,根据结果创建您的 ProcessFiles。

【讨论】:

【解决方案2】:

如果您认为是 .NET 实现导致了问题,那么我建议您使用 winapi 调用 _findfirst、_findnext 等。

在我看来,.NET 需要大量内存,因为列表完全复制到目录的每一级的数组中 - 所以如果你的目录结构有 10 级深,那么你有 10 个版本的数组文件对于结构中的每个目录,给定时刻和该数组的分配/解除分配。

对 _findfirst 等使用相同的递归技术只需要在每个递归级别都保留目录结构中某个位置的句柄。

【讨论】:

  • .NET 实现没有问题,至少在我的情况下没有体现。我只是想让这更快。
  • 我的意思是 NET 实现正在减慢执行速度;导致性能问题。
猜你喜欢
  • 1970-01-01
  • 2015-08-02
  • 1970-01-01
  • 1970-01-01
  • 2014-09-24
  • 1970-01-01
  • 2011-04-29
  • 1970-01-01
  • 2012-01-14
相关资源
最近更新 更多