如何改进此超快目录大小查找器
本文关键字:查找 何改进 | 更新日期: 2023-09-27 18:37:25
我有几个庞大的目录(由于遗留原因我无法重组)。
一个典型的目录可能包含 150K 个子目录,每个子目录都有嵌套目录,可能还有 4K 个文件。
我无法从 Windows 资源管理器或使用 du 通过 cygwin 获取目录大小。这些都只是继续处理数小时。
我已经编写了自己的代码来解决这个问题 - 对于较小的文件夹,我所拥有的速度非常快 - 但对于这些大型文件夹来说仍然很慢。
任何人都可以改进吗?
(如果您有完全不同的解决方案,我也很高兴听到它。
var size = GetDirectorySize3b(@"C:'MyMassiveFolder");
public long GetDirectorySize3b(string parentDirectory)
{
Int64 ttl = 0;
Stopwatch sw = new Stopwatch();
var dirs = Directory.GetDirectories(parentDirectory);
var llDirs = SplitIntoLists(dirs.ToList<string>(), 10);
ttl = ParallelDirSizeLLS(llDirs);
return ttl;
}
public List<List<string>> SplitIntoLists(List<string> l, int numLists)
{
List<List<string>> lls = new List<List<string>>();
int listLength = l.Count/numLists + 1;
for (int i = 0; i < l.Count; i += listLength)
{
var partL = l.Skip(i).Take(listLength).ToList<string>();
lls.Add(partL);
}
return lls;
}
public long ParallelDirSizeLLS(List<List<string>> lls)
{
_size = 0;
Parallel.ForEach(lls,
//new ParallelOptions { MaxDegreeOfParallelism = 30 },
ParallelDirSizeL);
return _size;
}
private void ParallelDirSizeL(List<string> l)
{
foreach (var dir in l)
{
var ds = GetDirectorySize3(dir);
Interlocked.Add(ref _size, ds);
}
}
public long GetDirectorySize3(string parentDirectory)
{
Scripting.FileSystemObject fso = new Scripting.FileSystemObject();
Scripting.Folder folder = fso.GetFolder(parentDirectory);
Int64 dirSize = (Int64)folder.Size;
Marshal.ReleaseComObject(fso);
return dirSize;
}
我不确定解决方案,但也许您可以尝试使用Microsoft索引服务?它存储有关所有索引文件的信息,包括大小。
我找到了一些信息:http://www.thejoyofcode.com/Using_Windows_Search_in_your_applications.aspx
为什么不使用文件系统观察器来监视目录并预先计算查询大小?也许在顶级目录中创建一个SQLite文件,并有一个包含所有文件和属性(包括大小)的表。如果创建/修改/删除了文件,FileSystemWatcher 可以通知你的应用,你可以更新数据库以进行快速查询。这只是一个想法。
由于存储设备同步执行 I/O,因此读取操作的并行化不会为您带来任何速度优势。
您的方法可能是将尽可能多的缓存到 RAM 中,然后并行处理。我们在我处理的项目中用于对 NTFS 上的文件进行操作的一种方法是缓存 MFT 记录。但是,我们有手写的文件系统解析代码,其中投入了大量工时,这不是您的解决方案。
因此,您可能想尝试找到为您完成此操作的源代码。此链接提到了 NTFS 的两个开源快速搜索实现,您可能会查看它们,因为它们执行完全相同的操作:在内存中缓存 MFT 以实现超快速搜索。它们不能直接解决您的问题,但似乎有该方法的源代码。
这是一个非常低级的解决方案,但在我看来,所有其他方法都会产生类似于已经讨论的结果,因为处理文件或文件夹的每个操作都尝试逐条记录读取MFT,通常大小为1KB。但是,磁盘处理一个(例如)2MB读取操作的速度比2048个1KB操作更快。此外,读取记录可能在物理上彼此靠近,在这种情况下,缓存也是一个好处。提到的产品这样做是为了搜索。但是您可以使用他们的代码来确定文件的大小。
这个基本的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吗?
我建议你应该采取一种非常不同的方法来解决问题。
我的解决方案基于收集文件夹包含的文件名的方法。获取子文件夹和文件的依赖于操作系统的方法对于大量文件相对较慢,因此您应该直接转到底层文件系统并从那里读取文件结构。
大多数Windows操作系统驱动器FS都是NTFS,并且有一个非常有效的库可以直接读取FS,我将在注释中放置一个指向库源的链接以及如何使用它的示例。但
我通常使用免费版本的树大小来获取大量文件夹结构的大小。这需要时间,但到目前为止一直提供:
树大小免费