【发布时间】:2013-08-27 15:02:10
【问题描述】:
我有一批大约 13,000 个 XML 文件(每天可能增长数百个),我需要使用 LINQ 过滤和将数据转换为我需要的数据进行处理,并将七种可能的事件类型中的每一种聚合到一个单一的事件类型文件(见下文)。所以,13k 文件分成 7 个文件。事件类型在 XML 中被很好地描述,因此过滤和聚合相对容易。然后,这些聚合文件将用于使用我已经编写的脚本为我们的数据库创建一个 MySQL 插入语句,该脚本也可以正常工作。
我有功能代码,它正在处理文件,但到目前为止它已经运行了 23 多个小时,看起来可能只完成了大约一半(?)。我忽略了放入文件计数器,所以我真的不知道,我不愿意再次重新启动它。我可以根据原始文件的大小(360mb 左右)与处理后的文件大小(180mb 左右)来做出有根据的猜测。我预计可能要运行大约六次,直到我们转储这种数据收集方法(使用 XML 文件作为数据库)并过渡到只使用 MySQL,所以我希望我能找到一种更有效的方法来处理文件。如果没有必要,我真的不想每次执行都花费 2 天以上的时间。
它在我的机器上本地运行,但仅在 1 HD(我认为是 10k RPM 梭子鱼)上运行。从一个驱动器读取并写入单独的驱动器可能会更快吗?我很确定我的瓶颈是由文件 IO 引起的,我正在打开和关闭文件数千次。也许我可以重构只打开一次阅读并在内存中做所有事情?我知道这会更快,但如果出现问题,我可能会丢失整个文件的数据。我仍然必须打开每个 13k 文件来读取它们、处理它们并写出到 XElement。
这是我正在运行的代码。我正在使用 LINQPad 并将代码作为 C# 语句运行,但如有必要,我可以将其转换为真正的可执行文件。 LINQPad 对于这样的原型制作非常方便!请让我知道 XML 的示例是否会使这更容易理解,但乍一看,它似乎并不密切。文件大小从 2k 到 285k 不等,但只有 300 个左右超过 100k,大多数在 25 - 50k 范围内。
string sourceDir = @"C:\splitXML\results\XML\";//source for the 13k files
string xmlDestDir = @"C:\results\XMLSorted\";//destination for the resultant 7 files
List<string> sourceList = new List<string>();
sourceList = Directory.EnumerateFiles(sourceDir, "*.xml", SearchOption.AllDirectories).ToList();
string destFile = null;
string[] events = { "Creation", "Assignment", "Modification", "Repair", "RepairReview", "Termination", "Test" };
foreach(string eventItem in events)
{
try
{
//this should only happen once the first time through and
//shouldn't be a continuing problem
destFile = Path.Combine(xmlDestDir, eventItem + "Uber.xml");
if (!File.Exists(destFile))
{
XmlTextWriter writer = new XmlTextWriter( destFile, null );
writer.WriteStartElement( "PCBDatabase" );
writer.WriteEndElement();
writer.Close();
}
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
foreach(var file in sourceList) //roughly 13k files
{
XDocument xd = XDocument.Load(file);
var actionEvents =
from e in xd.Descendants("PCBDatabase").Elements()
select e;
foreach(XElement actionEvent in actionEvents)
{
//this is where I think it's bogging down, it's constant file IO
var eventName =
from e in actionEvents.Elements()
select e.Name;
var eventType = eventName.First();
destFile = Path.Combine(xmlDestDir, eventType + "Uber.xml");
//another bottle neck opening each file thousands of times
XElement xeDoc = XElement.Load(destFile);
xeDoc.Add(actionEvent);
//and last bottle neck, closing each file thousands of times
xeDoc.Save(destFile);
}
}
【问题讨论】:
-
写入结果文件(更重要的是,每次你想添加一个元素时都加载它)确实是让你死的原因。将您想要写入的所有数据存储在内存中也是有问题的,如果没有其他原因,那么您可能没有没有足够的内存来执行此操作。你需要一个中间立场,这意味着批处理。读入几百个元素,将它们存储在内存中的结构中,然后一旦它变得足够大(尝试更改批处理大小以查看最有效的方法)将它们全部写入输出文件。跨度>
-
在这里大声思考,但我想知道如果您将
Uber设为MemoryMappedFile,您是否会获得更好的性能。 MMF 上的一些 more info。 -
感谢大家的回答,我会尝试实施您的想法,看看我能得到什么样的效率改进。 FWIW,我有一台 8 proc XEON 3.33 GHz,16GB 机器(专门为这个项目购买的正是这种类型的问题)
-
您是否总是希望附加到“Uber”xml 文件中?使用您的代码,Uber 文件仅在第一次创建,然后每次您运行它时,所有内容都会再次附加。这似乎很奇怪。我原以为您要么每次都想覆盖它们,要么只将新项目附加到 uber xml 文件中。
-
@mikez 是的,我总是需要追加,而不是写入整个文件。我错过了像 xeDoc.Append(destFile); 这样愚蠢的东西吗? ?