Профилирование производительности пользовательского класса логгера для приложения, читающего документ Word - PullRequest
2 голосов
/ 05 февраля 2020

У меня есть приложение, которое читает комментарии и абзацы из файла .docx, используя OpenXmlPowerTools. Это консольное приложение, которое создает файл debug.log во время работы.

Реализован класс регистратора, который сохраняет сообщения в текстовый файл во всех сборках и печатает эти сообщения на консоли для отладочных сборок. Следующий код является частью этого класса логгера:

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);
}

Я профилировал производительность этого, и для довольно большой текстовый документ с десятками комментариев, это заняло 1 мин 40 с. Без регистрации это заняло всего несколько секунд. После некоторого исследования кажется, что File.AppendText очень медленный. NET.

В качестве альтернативы я попытался использовать буфер:

using (StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8, 65536)
{
    // write the messages to the file
}

В отличие от информации в статье, которую я прочитал, где этот подход рекомендовался, производительность, казалось, ухудшилась (принимая более 2 минуты). Почему это? Как я могу улучшить производительность этого?

1 Ответ

1 голос
/ 05 февраля 2020

Ваш регистрационный код неверен. Вот так библиотека Entprise начала работать 15 лет go, и это не получилось. Используйте каркас логирования и покончите с этим.

Теперь к вашей актуальной проблеме. Вы открываете и закрываете файл для каждого вызова журнала, это слишком медленно и вызывает много накладных расходов. Оставьте файл журнала и StreamWriter открытым и используйте блокировку, чтобы гарантировать, что вы не будете одновременно записывать данные в файл журнала. Затем вам нужно решить проблемы с продолжительностью жизни, потому что, если FileStream будет завершен первым, ваш StreamWriter не сможет отправить sh ожидающие данные на диск, и вы потеряете последние сообщения журнала (скорее всего, важные с исключением cra sh. messsage).

Чтобы обойти этот грипп sh в каждом журнале, вызовите StreamWriter (медленно) или создайте класс-оболочку, который наследуется от CriticalFinalizerObject и удерживает ваш FileStream открытым, и вызывает G C .SuppressFinalize для Экземпляр FileStream для предотвращения раннего завершения во время завершения работы приложения.

Это наиболее распространенные ловушки, с которыми вы можете столкнуться при создании своей собственной библиотеки журналов.

Небольшой регистратор, например, здесь: https://github.com/Alois-xx/WMIWatcher/blob/master/FileLogger.cs

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...