如何加速大型xml文件读/写操作

时间:2017-04-25 07:29:42

标签: c# xml parallel-processing

我有一个Windows表单应用程序,目前执行以下操作:

1)指向一个目录并对其中的所有xml文件执行2(通常最多25个文件,范围从10mb到!5gb! - 不常见但可能)

2)xml读/写改变一些现有的xml属性(目前我使用一个后台工作者)

3)将更改的xml属性直接写入不同目录中的NEW文件

小应用程序工作正常,但完成需要太长时间(约20分钟,具体取决于净gb大小) 我随便尝试的是在Parallel.ForEach()中启动主要的rw方法,但它不会出乎意料地阻止自己退出

我的想法是通过在所有~25个文件上同时启动它来并行化读/写过程这是明智的吗?我怎么能这样做(TPL?)而不是把自己锁在外面?

PS:我有一台功能强大的台式机,拥有1TB三星专业版,16GB内存和英特尔酷睿i7

2 个答案:

答案 0 :(得分:1)

您可以使用此方法的ThreadPool

您可以拥有一个大小为20个文件的池 因为你有核心i7,你应该使用 TaskFactory.StartNew 在这种情况下,您应该在XMLProcessor之类的示例类中封装用于处理文件的代码 然后使用TaskFactory.StartNew,您可以使用多线程进行xml processsing

答案 1 :(得分:0)

这听起来像是通过PLINQ +异步lambdas进行数据并行的工作。

我最近需要处理来自zip存档的数据,该存档本身包含5,200个zip存档,然后每个存档包含一个或多个XML或CSV格式的数据文件。在解压缩和读入内存时总共有40-60 GB的数据。

算法浏览这些数据,根据它与提供的谓词一起找到的内容做出决策,最后将选择内容写入1.0-1.5 GB文件。使用具有32个处理器的异步PLINQ模式,每个输出文件的平均运行时间为4.23分钟。

在使用async PLINQ实现简单的解决方案之后,我花了一些时间来尝试通过深入研究TPL和TPL Dataflow库来改善运行时间。最后,尝试击败async PLINQ被证明是一种有趣但最终无用的运动来满足我的需求。来自更“优化”的解决方案的性能利润不值得增加复杂性。

以下是异步PLINQ模式的示例。初始集合是一个文件路径数组。

在第一步中,将每个文件路径异步读入内存并进行解析,将文件名缓存为根级属性,然后流式传输到下一个函数。

在最后一步中,每个XElement都异步写入新文件。

我建议您使用读取文件的lambda。在我的例子中,我发现通过异步lambda读取在解压缩内存中的文件时给了我更好的吞吐量。

但是,对于简单的XML文档,最好用方法调用XElement.Load(string file)替换第一个异步lambda,然后根据需要读取PLINQ。

using System.IO;
using System.Linq;
using System.Xml.Linq;

namespace AsyncPlinqExample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            // Limit parallelism here if needed 
            int degreeOfParallelism = Environment.ProcessorCount;

            string resultDirectory = "[result directory path here]";

            string[] files = Directory.GetFiles("[directory with files here]");

            files.AsParallel()
                 .WithDegreeOfParallelism(degreeOfParallelism)
                 .Select(
                     async x =>
                     {
                         using (StreamReader reader = new StreamReader(x))
                         {
                             XElement root = XElement.Parse(await reader.ReadToEndAsync());

                             root.SetAttributeValue("filePath", Path.GetFileName(x));

                             return root;
                         }
                     })
                 .Select(x => x.Result)
                 .Select(
                     x =>
                     {
                         // Perform other manipulations here

                         return x;
                     })
                 .Select(
                     async x =>
                     {
                         string resultPath = 
                             Path.Combine(
                                 resultDirectory,
                                 (string) x.Attribute("fileName"));

                         await Console.Out.WriteLineAsync($"{DateTime.Now}: Starting {(string) x.Attribute("fileName")}.");

                         using (StreamWriter writer = new StreamWriter(resultPath))
                         {
                             await writer.WriteAsync(x.ToString());
                         }

                         await Console.Out.WriteLineAsync($"{DateTime.Now}: Comleted {(string)x.Attribute("fileName")}.");
                     });
        }
    }
}