C# 如何改进这个超高速目录大小查找器?

C# 如何改进这个超高速目录大小查找器?,c#,parallel-processing,directory,size,C#,Parallel Processing,Directory,Size,我有几个大目录(由于遗留原因,我无法重组) 一个典型的目录可能包含150K个子目录,每个子目录都有嵌套目录,可能还有4K文件 我无法从windows资源管理器或使用du通过cygwin获取目录大小。这两个都只是继续处理几个小时 我已经编写了自己的代码来解决这个问题——对于较小的文件夹,我的代码非常快——但对于这些大型文件夹,我的代码仍然很慢 有人能改进吗 (如果你有一个完全不同的解决方案,我也很高兴听到。) var size=GetDirectorySize3b(@“C:\MyMassiveFo

我有几个大目录(由于遗留原因,我无法重组)

一个典型的目录可能包含150K个子目录,每个子目录都有嵌套目录,可能还有4K文件

我无法从windows资源管理器或使用du通过cygwin获取目录大小。这两个都只是继续处理几个小时

我已经编写了自己的代码来解决这个问题——对于较小的文件夹,我的代码非常快——但对于这些大型文件夹,我的代码仍然很慢

有人能改进吗

(如果你有一个完全不同的解决方案,我也很高兴听到。)

var size=GetDirectorySize3b(@“C:\MyMassiveFolder”);
公共长GetDirectorySize3b(字符串parentDirectory)
{
Int64 ttl=0;
秒表sw=新秒表();
var dirs=Directory.GetDirectories(parentDirectory);
var llDirs=拆分列表(dirs.ToList(),10);
ttl=平行DirSizells(LLDir);
返回ttl;
}
公共列表拆分为列表(列表l,整数列表)
{
List lls=新列表();
int listLength=l.Count/numLists+1;
for(int i=0;i
我不确定解决方案,但也许您可以尝试使用Microsoft索引服务?它存储所有索引文件的信息,包括大小

我发现了一些信息:

由于存储设备同步进行I/O,因此读操作的并行化不会带来任何速度优势

您的方法可能是将尽可能多的缓存到RAM中,然后并行处理。我们在我处理NTFS文件操作的项目中使用的一种方法是缓存MFT记录。但是,我们已经手工编写了文件系统解析代码,并投入了大量的人力,这不是您的解决方案

因此,您可能希望尝试找到为您做这件事的源代码。这里提到了两个针对NTFS的开源快速搜索实现,您可能会看到这两个实现,因为它们做的正是:将MFT缓存在内存中以进行超快速搜索。它们不会直接解决您的问题,但似乎有该方法的源代码

这是一个相当低级的解决方案,但在我看来,其他每种方法的结果都与前面讨论的类似,因为处理文件或文件夹的每个操作都会尝试逐个记录读取MFT记录,通常是1KB大小。然而,磁盘处理一个2MB的读取操作比2048个1KB的操作快。此外,读取记录可能在物理上彼此相邻,在这种情况下,缓存也是一种好处。
提到的产品都是为了搜索。但是您可以使用它们的代码来确定文件的大小。

为什么不使用FileSystemWatcher来监视目录并预先计算查询大小呢?。可能在顶层目录中创建一个SQLite文件,并有一个包含所有文件和属性(包括大小)的表。如果文件被创建/修改/删除,FileSystemWatcher可以通知您的应用程序,您可以更新数据库以进行快速查询。这只是一个想法。

这个基本java类:

import java.io.File;
import java.util.concurrent.atomic.AtomicLong;

public class DirSize {

    private static AtomicLong l = new AtomicLong();
    private static AtomicLong files = new AtomicLong();
    private static AtomicLong dirs = new AtomicLong();

    public static void recurse(File f) {
        if(f==null) {
            return;
        }
        if(f.isDirectory()) {
            dirs.getAndIncrement();
            if(f.listFiles()==null) {
                return;
            }
            for(File fc : f.listFiles()) {
                recurse(fc);
            }
        } else {
            files.getAndIncrement();
            l.getAndAdd(f.length());
        }
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        recurse(new File("/usr"));
        long end = System.currentTimeMillis();
        System.out.println(end-start+" ms");
        System.out.println(files.get()+" files");
        System.out.println(dirs.get()+" dirs");
        System.out.println("size: "+l.get());
        System.out.println("size: "+(l.get()/(1024*1024))+" MB");
        double secs = (double)(end-start) / 1000d;
        double f = (double)files.get();
        System.out.println(Math.round(f/secs)+" files/s ");
    }

}
给我:

11631 ms
386589 files
33570 dirs
size: 93068412461
size: 88756 MB
33238 files/s 
第一次运行时(但操作系统未重新启动)。这是macbook pro上的macOS,其SSD的顺序读写速度高于700 MB/s。这里的要点可能比SSD基本上没有寻道时间这一事实要小,因为读取文件大小是一个IOP,但很小


你在什么磁盘上运行?什么文件系统?它必须是窗户吗

事实上,我建议你应该采取完全不同的方法来解决这个问题

我的解决方案基于收集文件夹包含的文件名的方法。对于大量文件,获取子文件夹和文件的依赖操作系统的方法相对较慢,因此您应该直接转到底层文件系统并从那里读取文件结构

大多数Windows OS驱动器FS都是NTFS,并且有一个非常高效的库可以直接读取FS,我将在注释中提供一个指向该库源代码的链接以及如何使用该库的示例。
但是

我通常使用免费版本的树大小来获得大量文件夹结构的大小。这需要时间,但到目前为止,它始终提供:


由于它将绑定IO,使用多个线程可能会使其速度变慢。不,它确实有所改进,但最多只能使用10个线程。这是一个非常非常严重的错误。它看起来很快,因为您实际上没有从磁盘读取数据,这是一种标准的测试风险。重新启动计算机并再次运行程序。第一次运行非常糟糕,这是您可以从这样的代码中获得的性能。使用线程会使情况变得更糟,因为您只有一个磁盘,而且它不喜欢让多个线程满意。寻找是迄今为止你能用磁盘做的最昂贵的事情。@Hans-好吧,但我该怎么做呢
11631 ms
386589 files
33570 dirs
size: 93068412461
size: 88756 MB
33238 files/s