【问题标题】:Minimize performance impact of event logging?最小化事件记录的性能影响?
【发布时间】:2010-04-21 17:02:30
【问题描述】:

我有一个静态日志记录函数,它使用 StringBuilder 在将字符串发送到日志之前连接一堆查询参数。这个过程可能会变得相当长,因为我们可能有大约 10 个参数(.Append 调用)并且最终有大约 200 个字符长。

我想尽量减少日志记录功能对性能的影响。 (每个网络请求可能会多次调用此日志功能,我们会测量每个网络请求的处理时间)

如何/应该/我可以建立一个 StringBuilders 的“池”来提高性能吗?

我也可以异步执行所有这些日志记录,对吗?我该怎么做?

【问题讨论】:

  • 我敢打赌,真正的性能影响是 IO,而不是 StringBuilder。
  • 我敢打赌,日志记录根本没有真正的影响。

标签: .net performance optimization logging


【解决方案1】:

如果您预计日志记录活动会爆发,并且如果事件日志记录确实导致了您测量的性能问题(请参阅优化/设计级别的“级别”下的 Premature Optimization),您可以创建一个日志记录请求队列并一个在队列外工作的单独线程。如果队列已满,您可能希望队列阻止调用者,直到它未满为止。

如果您的日志记录请求相当稳定,而不是活动爆发,那么如果通常有一个 CPU 内核没有在其他情况下使用,您仍然会总体上有所收获。如果所有 CPU 内核通常都在大量使用,单独的线程只会增加开销和复杂性,而不会带来好处。

【讨论】:

  • 问题是我需要在发送到队列之前从对象中提取字符串。 (已经设置好了)
  • 为什么不直接将对象放入队列中?
  • 更改队列以接受对象并在其上调用 ToString。
  • 或者让客户端调用 .ToString 并使用现有队列。我同意 Fernando 和 Steven 的 cmets 的观点,如果日志记录存在真正的瓶颈(不太可能),那么最可能的罪魁祸首是 IO 而不是 StringBuilder。
【解决方案2】:

由于日志记录通常依赖于状态,因此将日志条目的实际构造放到另一个线程中通常不是一种选择。但是,您可以捕获相关数据以异步格式化。虽然我不会在这里实现整个日志记录机制(尽管您可以查看我的 ProcessQueue article on CodeProject 以使其更容易),但您可以执行以下操作:

public static void LogAsync<T1>(T1 value, Func<T1, string> formatter)
{
    // asynchronously call formatter(value) and log the result
}

public static void LogAsync<T1, T2>(T1 value1, T2 value2, Func<T1, T2, string> formatter)
{
    // asynchronously call formatter(value1, value2) and log the value
}

...and so on

如果字符串构造是阻碍日志记录的原因,那么(假设各种T 类型是不可变的,或者至少不要在调用LogAsync 和何时更改formatter 被调用)这应该可以缓解这种情况。

【讨论】:

    【解决方案3】:

    我强烈建议在这些情况下使用诸如 Nlog 之类的日志库,它们具有高度的灵活性和高性能,您还可以专注于您的业务逻辑。感谢您可能认为第三方库会很慢,但这不是我的经验,尤其是在 Nlog 方面。

    另一个选项是 log4cpp,虽然这还没有真正在开发中

    【讨论】:

      【解决方案4】:

      您可能想要的是一个 StringBuilder 实例,用于您的应用正在处理的每个 HttpRequest。一个简单的方法是在 Global.asax 中创建这个 StringBuilder。

      您不必担心此字符串生成器上的同步问题,因为在任何给定时间,它只能由一个 httpRequest 访问。

      请求处理完成后,您必须将 stringBuilder 的内容推送到某个中心位置(日志),您将不得不担心同步问题,但如果您在 OnEndRequest 事件中执行此操作,则会产生影响将是最小的。

      在将内容移至日志后不要忘记清理 stringBuilder

      【讨论】:

        【解决方案5】:

        不,您不应该汇集 StringBuilder 对象。它们的创建和使用成本低廉。如果您将它们汇集在一起​​,您只会将它们从短寿命对象转变为长寿命对象。实际上,垃圾收集器将一个 StringBuilder 移到下一代以保留它比清理一大堆它们要多。

        StringBuilders 的使用绝对不会是瓶颈。大部分工作是将字符串存储在日志中,无论它在哪里。

        异步进行日志记录本身不会为您节省任何性能。服务器仍然需要做同样的工作,而你只是增加了创建另一个线程的开销。

        如果你保留一个要记录的字符串缓冲区,并同时存储一堆字符串,那么它可能会更有效。当然,前提是存储多个字符串实际上可以比存储一个和一个更有效。

        您可以将字符串列表保存在静态变量中,并在您有足够的字符串时存储它们。 (当然要同步访问,因为多个线程可以访问它。)或者简单地从一个 Web 请求中收集字符串并保存在一个,这更容易,但仍然可以为您节省一些工作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-30
          • 2014-09-18
          • 1970-01-01
          • 1970-01-01
          • 2021-08-01
          • 1970-01-01
          相关资源
          最近更新 更多