在.net中解析高性能文本文件

时间:2010-03-20 06:11:37

标签: .net performance optimization string

情况如下:

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

我用一个有几千个请求的日志文件测试了它(10000到20000之间并不确切)

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

这占用了大部分资源。

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

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

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

string.tolower - 各种用途

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

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

还有一些我能够替换的。例如字典getter是 也需要很多资源。我没有预料到它是一本字典,应该将其密钥编入索引。我用一个多维数组替换它并节省了一些cpu时间。

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

现在这真的很糟糕。

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

那么我的替代方案是什么?如果有的话,因为我认为这只是一个.net限制,我不能做太多。

修改

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

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

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

CPU:amd phenom II x2 545 3ghz。

不是多线程的

6 个答案:

答案 0 :(得分:4)

如果没有看到你的代码,就很难知道你是否有任何错误导致你的表现费用。在没有看到一些样本数据的情况下,我们无法合理地尝试实验来了解我们自己的表现。

之前你的词典键是什么?移动到一个多维数组听起来像一个奇怪的举动 - 但我们需要更多的信息来了解你之前对数据做了什么。

请注意,除非您明确并行化工作,否则拥有双核机器不会有任何区别。如果你真的是CPU绑定的那么你可以并行化 - 尽管你需要仔细考虑;你很可能想读一个文本的“块”(几行),并要求一个线程解析它,而不是一次一行。结果代码可能要复杂得多。

我不知道10000行的一秒是否合理,说实话 - 如果您可以发布一些样本数据以及您需要做些什么,我们可以提供更有用的反馈。

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

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

但是,要优化解析过程:

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

可能有其他的事情,但我恐怕现在没有时间进入他们:(

答案 1 :(得分:2)

你看过memory mapped files了吗? (尽管在.NET 4.0中也是如此)

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

你可以做几件事:

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

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