分析自定义记录器 class 的性能,用于读取 Word 文档的应用程序
Profling performance of custom logger class, for an application reading Word document
我有一个应用程序,它使用 OpenXmlPowerTools
从 .docx 文件中读取注释和段落。它是一个控制台应用程序,它会在 运行 时创建一个 debug.log 文件。
实现了一个记录器 class,它将消息保存到所有构建的文本文件中,并将这些消息打印到调试构建的控制台。以下代码是此记录器的一部分 class:
public static class Logger
{
public enum LogLevel
{
ERROR, WARNING, DEBUG
}
public static void Log(string message, LogLevel level, bool newline)
{
try
{
// the very next line was a hotspot, as shown in the profiler
using (StreamWriter sw = File.AppendText(path))
{
// write the messages to this file
}
}
catch (Exception ex)
{
// handle it
// I know it is bad practice to catch System.Exception, I need to fix this.
}
}
}
在代码中,这个函数经常这样调用:
private void doSomething(string someParameter)
{
Logger.Log("The parameter is: " + someParameter, Logger.LogLevel.DEBUG, true);
}
我已经分析了它的性能,对于一个有几十条评论的相当大的 word 文档,它需要 1 分 40 秒才能完成。没有记录,只用了几秒钟。经过一些调查,似乎 File.AppendText
在 .NET 中非常慢。
作为替代方案,我尝试使用缓冲区:
using (StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8, 65536)
{
// write the messages to the file
}
与我读过的一篇推荐这种方法的文章中的信息相反,性能似乎变差了(耗时超过 2 分钟)。为什么是这样?我怎样才能提高它的性能?
您的登录代码有误。 15 年前,Entprise Library 就是这样开始的,但结果并不好。
使用日志记录框架并完成它。
现在谈谈你的实际问题。您为每个日志调用打开和关闭文件,这非常慢并且会导致大量开销。保持 Log 文件和 StreamWriter 打开并使用锁来确保您不会同时向日志文件写入数据。
接下来您需要处理生命周期问题,因为如果 FileStream 首先完成,您的 StreamWriter 将无法将挂起的数据刷新到磁盘并且您丢失了最后的日志消息(很可能是重要的崩溃异常消息)。
要在每个日志上解决刷新问题,调用 StreamWriter(慢)或创建一个包装器 class,它派生自 CriticalFinalizerObject 并保持您的 FileStream 打开并在 FileStream 实例上调用 GC.SuppressFinalize 以防止在应用程序关闭期间提前完成。
这是您在创建自己的日志记录库时最常遇到的陷阱。
一个小型记录器例如这里:
https://github.com/Alois-xx/WMIWatcher/blob/master/FileLogger.cs
我有一个应用程序,它使用 OpenXmlPowerTools
从 .docx 文件中读取注释和段落。它是一个控制台应用程序,它会在 运行 时创建一个 debug.log 文件。
实现了一个记录器 class,它将消息保存到所有构建的文本文件中,并将这些消息打印到调试构建的控制台。以下代码是此记录器的一部分 class:
public static class Logger
{
public enum LogLevel
{
ERROR, WARNING, DEBUG
}
public static void Log(string message, LogLevel level, bool newline)
{
try
{
// the very next line was a hotspot, as shown in the profiler
using (StreamWriter sw = File.AppendText(path))
{
// write the messages to this file
}
}
catch (Exception ex)
{
// handle it
// I know it is bad practice to catch System.Exception, I need to fix this.
}
}
}
在代码中,这个函数经常这样调用:
private void doSomething(string someParameter)
{
Logger.Log("The parameter is: " + someParameter, Logger.LogLevel.DEBUG, true);
}
我已经分析了它的性能,对于一个有几十条评论的相当大的 word 文档,它需要 1 分 40 秒才能完成。没有记录,只用了几秒钟。经过一些调查,似乎 File.AppendText
在 .NET 中非常慢。
作为替代方案,我尝试使用缓冲区:
using (StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8, 65536)
{
// write the messages to the file
}
与我读过的一篇推荐这种方法的文章中的信息相反,性能似乎变差了(耗时超过 2 分钟)。为什么是这样?我怎样才能提高它的性能?
您的登录代码有误。 15 年前,Entprise Library 就是这样开始的,但结果并不好。 使用日志记录框架并完成它。
现在谈谈你的实际问题。您为每个日志调用打开和关闭文件,这非常慢并且会导致大量开销。保持 Log 文件和 StreamWriter 打开并使用锁来确保您不会同时向日志文件写入数据。 接下来您需要处理生命周期问题,因为如果 FileStream 首先完成,您的 StreamWriter 将无法将挂起的数据刷新到磁盘并且您丢失了最后的日志消息(很可能是重要的崩溃异常消息)。
要在每个日志上解决刷新问题,调用 StreamWriter(慢)或创建一个包装器 class,它派生自 CriticalFinalizerObject 并保持您的 FileStream 打开并在 FileStream 实例上调用 GC.SuppressFinalize 以防止在应用程序关闭期间提前完成。
这是您在创建自己的日志记录库时最常遇到的陷阱。
一个小型记录器例如这里: https://github.com/Alois-xx/WMIWatcher/blob/master/FileLogger.cs