【问题标题】:High performance text file parsing in .net.net 中的高性能文本文件解析
【发布时间】:2011-01-29 17:50:09
【问题描述】:

情况如下:

我正在制作一个小程序来解析服务器日志文件。

我用一个包含数千个请求的日志文件对其进行了测试(10000 - 20000 之间不知道确切)

我要做的就是将日志文本文件加载到内存中,以便我可以查询它们。

这占用了最多的资源。

占用cpu时间最多的方法是那些(最坏的罪魁祸首):

string.split - 将行值拆分为值数组

string.contains - 检查用户代理是否包含特定的代理字符串。 (确定浏览器 ID)

string.tolower - 各种用途

streamreader.readline - 逐行读取日志文件。

string.startswith - 判断 line 是列定义行还是带值的行

还有一些我可以替换的。例如字典吸气剂是 也占用了很多资源。这是我没想到的,因为它是一本字典,应该索引它的键。我将其替换为多维数组并节省了一些 cpu 时间。

现在我在快速双核上运行,加载我提到的文件所需的总时间约为 1 秒。

现在这真的很糟糕。

想象一个每天有数万人访问的网站。加载日志文件需要几分钟时间。

那么我的替代方案是什么?如果有的话,因为我认为这只是一个 .net 限制,我对此无能为力。

编辑:

如果你们中的一些大师想查看代码并发现问题,这里是我的代码文件:

到目前为止,占用资源最多的函数是 LogEntry.New 加载所有数据的函数称为Data.Load

创建的 LogEntry 对象总数:50 000。所用时间:0.9 - 1.0 秒。

CPU:AMD 飞鸿 II x2 545 3ghz。

不是多线程的

【问题讨论】:

    标签: .net performance optimization string


    【解决方案1】:

    如果不查看您的代码,就很难知道您是否有任何错误会影响您的性能。在没有看到一些样本数据的情况下,我们无法合理地尝试实验来了解自己的表现。

    你以前的字典键是什么?迁移到多维数组听起来很奇怪 - 但我们需要更多信息才能知道您之前对数据做了什么。

    请注意,除非您明确将工作并行化,否则拥有双核机器不会有任何区别。如果你真的受 CPU 限制,那么你可以并行化 - 尽管你需要小心地这样做;您很可能想要阅读一段文本(几行)并要求一个线程解析它,而不是一次传递一行。不过,生成的代码可能要复杂得多。

    老实说,我不知道 10,000 行一秒是否合理 - 如果您可以发布一些示例数据以及您需要如何处理,我们可以提供更有用的反馈。

    编辑:好的,我已经快速浏览了代码。一些想法...

    最重要的是,这可能不是您应该“按需”执行的操作。相反,定期解析为后台进程(例如,当日志翻转时)并将感兴趣的信息放入数据库中 - 然后在需要时查询该数据库。

    但是,要优化解析过程:

    • 我个人不会继续检查 StreamReader 是否在末尾 - 只需调用 ReadLine 直到结果为 Nothing
    • 如果您希望“#fields”行先出现,请在循环外阅读。这样您就不需要查看您是否已经在每次迭代中获得了这些字段。
    • 如果您知道一行非空,可能测试第一个字符是 '#' 可能比调用 line.StartsWith("#") 更快 - 我必须测试。李>
    • 每次都在扫描字段,您要求提供日期、时间、URI 词干或用户代理;相反,当您解析“#fields”行时,您可以创建一个新的 LineFormat 类的实例,该类可以应对任何字段名称,但特别记住您知道的字段索引'重新想要。这也避免了为每个日志条目复制完整的字段列表,这非常浪费。
    • 拆分字符串时,您会获得比正常情况更多的信息:您知道需要多少字段,并且知道您只拆分单个字符。您可能会为此编写一个优化版本。
    • 单独解析日期和时间字段然后组合结果可能会更快,而不是连接它们然后解析。我必须测试一下。
    • 多维数组比单维数组慢得多。如果您确实想坚持“复制每个条目的所有字段名称”的想法,则值得将其分成两个数组:一个用于字段,一个用于值。

    可能还有其他的事情,但我现在恐怕没有时间深入研究它们了:(

    【讨论】:

    • 添加了代码文件,喜欢的可以看看谢谢:)
    【解决方案2】:

    你已经看过memory mapped files了吗? (不过在 .NET 4.0 中)

    编辑 :- 另外,是否可以将这些大文件拆分为较小的文件并解析较小的文件。这是我们在一些大文件中所做的事情,这比解析巨型文件要快。

    【讨论】:

    • 看来问题不是IO而是cpu。顺便说一句,我有一个 SSD 硬盘。
    【解决方案3】:

    您可以尝试正则表达式。或者更改业务流程,以便更方便地以该速度加载文件。

    【讨论】:

    • 前段时间我用正则表达式替换了一些函数,它比较慢。我在某个地方还有一点小进步,但机会很小。
    • 你编译了正则表达式吗?
    • 如:Regex r = new Regex(@"(my)? +regex", RegexOptions.Compiled);
    【解决方案4】:

    您可以尝试延迟加载:例如,一次读取文件 4096 个字节,查找行尾并将所有行尾保存在一个数组中。现在,如果您的程序的某些部分需要 LogEntry N,请查找该行的起始位置,读取它并动态创建一个 LogEntry 对象。 (使用内存映射文件会更容易一些。)作为可能的优化,如果调用代码通常需要连续的 LogEnties,您的代码可以例如自动预读接下来的 100 个日志条目。您可以缓存最近访问的 1000 个条目。

    【讨论】:

    • 嗯,我需要我能得到的所有日志条目。因为在加载完之后,我必须显示所有请求、浏览量、访问者等的计数。如果它们没有加载到内存中,我无法计算它们。例如,要计算浏览量,我必须加载用户代理,我不能只计算行数。
    【解决方案5】:

    您是否考虑过将日志条目加载到数据库中并从那里查询?这样,您就可以跳过解析已存储在数据库中的日志条目。

    【讨论】:

      【解决方案6】:

      你可以做几件事:

      每次更改日志时都会持续解析日志的 Windows 服务。然后您的 UI 请求此服务。

      或者你可以每分钟或更长时间解析一次并缓存结果,你真的需要它是实时的吗?也许只需要解析一次?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-25
        • 1970-01-01
        • 1970-01-01
        • 2012-02-12
        • 1970-01-01
        • 2014-01-15
        相关资源
        最近更新 更多