我有几个大型目录(由于遗留原因我无法进行重组)。
一个典型的目录可能包含150K个子目录,每个子目录都有嵌套目录,也可能是4K文件。
我无法从Windows资源管理器或通过cygwin使用du获取目录大小。这些都只是持续处理数小时。
我已经编写了自己的代码来解决这个问题 - 而我所拥有的代码对于较小的文件夹来说速度非常快 - 但对于这些大型文件夹来说仍然很慢。
任何人都可以改进吗?
(如果你有一个完全不同的解决方案,我也很高兴听到它。)
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;
}
答案 0 :(得分:1)
我不确定解决方案,但也许您可以尝试使用Microsoft Indexing Service?它存储有关所有索引文件的信息,包括大小。
我找到了一些信息: http://www.thejoyofcode.com/Using_Windows_Search_in_your_applications.aspx
答案 1 :(得分:1)
由于存储设备同步执行I / O操作,因此您将无法通过读取操作的并行化获得任何速度优势。
您的方法可能是尽可能多地缓存到RAM中,然后并行处理。我在项目上使用的方法,我在NTFS上使用文件进行操作是缓存 MFT记录。但是,我们手写的文件系统解析代码中包含了大量的工时,这对您来说不是解决方案。
所以你可能想尝试找到适合你的源代码。这个link提到了NTFS的两个开源快速搜索实现,您可能会看到它们,因为它们完成了一切:在内存中缓存MFT以进行超快速搜索。它们不直接解决您的问题,但似乎有该方法的源代码。
这是非常低级别的解决方案,但在我看来,每个其他方法都会有类似于已经讨论过的结果,因为处理文件或文件夹的每个操作都会尝试按记录读取MFT记录,通常大小为1KB。但是,磁盘处理一个,比如2MB读取操作,然后是2048个1KB操作。读取记录也可以在物理上彼此靠近,在这种情况下,缓存也是一个好处。 提到的产品用于搜索。但您可以使用他们的代码来确定文件&#39;尺寸。
答案 2 :(得分:1)
为什么不使用FileSystemWatcher监视目录并预先确定查询大小?也许在顶级目录中创建一个SQLite文件,并拥有一个包含所有文件和属性的表,包括大小。如果创建/修改/删除了文件,FileSystemWatcher可以通知您的应用程序,您可以更新数据库以进行快速查询。这只是一个想法。
答案 3 :(得分:0)
这个基本的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,带有顺序读取和写入超过700 MB / s的SSD,这里的点可能比SSD基本上没有寻道时间的事实要少,因为读取文件大小是IOP,但是一个小小的。
你在运行什么磁盘?什么文件系统?它必须是窗户吗?
答案 4 :(得分:0)
实际上我建议你采取一种非常不同的方法来解决问题。
我的解决方案基于收集文件夹所包含的文件名的方式。获取子文件夹和文件的os依赖方法对于大量文件来说相对较慢,因此您应该直接进入底层文件系统并从那里读取文件结构。
大多数Windows操作系统驱动器FS都是NTFS,并且有一个非常高效的库来直接读取FS,我将链接到库的源代码以及如何在注释中使用它的示例。 但是
答案 5 :(得分:0)
我通常使用Tree Size的免费版本来获取海量文件夹结构的大小。它花了很长时间,但到目前为止一直在提供: