我需要获取文件列表,将它们修剪到目录路径并返回一个不同的列表。在某些情况下,这可能会处理超过五百万个文件。
我遇到的问题是,由于我无法确定的原因,核心进程会因0%的CPU使用率而挂起。
var filePaths = File.ReadAllLines("list_of_files.txt");
// ...
blockSw.Restart();
int[] curCounter = new int[1];
Stopwatch groupSw = Stopwatch.StartNew();
Parallel.For(0, filePaths.LongLength, i =>
{
//Trim the filename, if it exists, off of every
// entry that we read out of the input file
filePaths[i] = (Path.GetDirectoryName(filePaths[i]));
//This can be used to safely report status
// little hack-y, though
lock (curCounter)
{
curCounter[0]++;
if (curCounter[0] % 100000 == 0)
{
Trace.WriteLine(curCounter[0].ToString() + " rows complete in "
+ groupSw.ElapsedMilliseconds
+ " ; total time: " + blockSw.ElapsedMilliseconds);
groupSw.Restart();
}
}
}
);
blockSw.Stop();
Trace.WriteLine("Completed path truncation in " + blockSw.ElapsedMilliseconds + "ms.");
输出看起来像这样:
100000 rows complete in 266 ; total time: 266
200000 rows complete in 239 ; total time: 507
300000 rows complete in 843 ; total time: 1351
400000 rows complete in 1058 ; total time: 2411
...
1100000 rows complete in 3480 ; total time: 11602
1200000 rows complete in 432 ; total time: 12036
1300000 rows complete in 342 ; total time: 12379
...
4800000 rows complete in 832 ; total time: 48617
4900000 rows complete in 377 ; total time: 48996
5000000 rows complete in 2841 ; total time: 51839
5100000 rows complete in 1285 ; total time: 53126
Completed path truncation in 148124ms.
注意最后两行... 53秒通过所有内容,然后循环结束,我们坐下来等待〜90秒。在TaskManager中观察进程我可以看到它在这段时间内在0%CPU处空闲。
关于这里发生了什么或者我可能寻找线索的任何线索?
列出文件路径的输入文件大约为400MB,在此过程中,TaskManager报告的内存大小约为900MB。在测试期间,有足够的免费物理RAM可用于此数量。
取出环路状态报告不会改变性能 - 在循环结束时,我们仍然会在0%CPU使用率的情况下暂停约90秒。
标准for
循环而不是Parallel.For
我遇到了同样的问题。
感谢克里斯,杰克和汉斯。由于克里斯的意见,他无法复制和汉斯对Break All的建议,我能够缩小问题范围。进一步调试,我发现实际问题是Path.GetDirectoryName
是罪魁祸首。虽然它几乎在每个文件路径上运行0-15ms,但有几十条路径需要花费2分钟来处理。我注意到这些路径都包含〜。我仍然不清楚为什么它在没有使用CPU的情况下这样做但是我已经足够理解它是Path
内部的,加速它的唯一方法就是重新实现{{1} }。
答案 0 :(得分:0)
至于不同。
可能不会比所有这些更快,然后单个LINQ,但它应该是更少的内存。
using (StreamReader sr = new StreamReader("TestFile.txt"))
{
String line;
String path;
HashSet<string> paths = HashSet<string>(StringComparer.OrdinalIgnoreCase);
// Read and process lines from the file until the end of
// the file is reached.
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
path = Path.GetDirectoryName(line);
if(!String.IsNullOrEmpty(path)) paths.Add(path.Trim());
}
}