Проблемы с производительностью в FileStream.Write при записи байтов, декодированных с использованием AsciiEncoding.GetBytes и Convert.FromBase64String - PullRequest
4 голосов
/ 15 мая 2009

У меня проблема с производительностью при использовании функции FileStream.Write.

У меня есть консольное приложение, которое я использую для чтения строки Base64 из файла (размер ~ 400 КБ) с использованием объекта StreamReader. Я преобразую эту строку в байтовый массив, используя Convert.FromBase64String. Затем я записываю этот байтовый массив в файл, используя объект FileStream. Длина байтового массива, полученная здесь, составила 334991.

Я измерил время, затрачиваемое на запись байтового массива, и оно составляет примерно 0,116 секунды.

Ради интереса, я получил массив байтов из той же строки в кодировке Base64, используя функцию ASCIIEncoding.GetBytes (хотя я знал, что это не даст правильный вывод DECODED - я просто хотел попробовать). Я записал этот байтовый массив в файл, используя объект FileStream. Длина полученного массива байтов была 458414.

Я измерил время, затраченное на запись байтового массива, используя эту методологию, и оно составляет примерно 0,008 секунды.

Вот пример кода:

class Program
{
    static void Main(string[] args)
    {
        Stopwatch stopWatch = new Stopwatch();
        TimeSpan executionTime;

        StreamReader sr = new StreamReader("foo.txt");
        string sampleString = sr.ReadToEnd();
        sr.Close();

        ////1. Convert to bytes using Base64 Decoder (The real output!)
        //byte[] binaryData = Convert.FromBase64String(sampleString);

        //2. Convert to bytes using AsciiEncoding (Just for Fun!)
        byte[] binaryData = new System.Text.ASCIIEncoding().GetBytes(sampleString);
        Console.WriteLine("Byte Length: " + binaryData.Length);

        stopWatch.Start();
        FileStream fs = new FileStream("bar.txt", FileMode.Create, FileAccess.Write);
        fs.Write(binaryData, 0, binaryData.Length);
        fs.Flush();
        fs.Close();
        stopWatch.Stop();

        executionTime = stopWatch.Elapsed;
        Console.WriteLine("FileStream Write - Total Execution Time: " + executionTime.TotalSeconds.ToString());
        Console.Read();
    }
}

Я выполнил тесты примерно для 5000 файлов, содержащих строку в кодировке Base64, и разница между временем, затрачиваемым на запись этих двух типов байтового массива, почти в 10 раз (с одним при записи байтового массива с использованием real декодирование занимает больше времени).

Длина байтового массива, полученного с помощью Convert.FromBase64String, меньше длины массива, полученного с помощью функции ASCIIEncoding.GetBytes.

Интересно, что все, что я пытаюсь сделать, это написать группу байтов, используя объект FileStream. Так почему же при записи байтового массива на диск должна быть такая резкая разница в производительности (затраченное время)?

Или я что-то делаю ужасно неправильно? Пожалуйста, порекомендуйте.

Ответы [ 4 ]

1 голос
/ 15 мая 2009

Я думаю, что основная проблема в вашем коде в том, что вы пытаетесь сравнить капусту с морковью (французское выражение):

Convert.FromBase64String и ASCIIEncoding (). GetBytes вообще не делает одно и то же.

Просто попробуйте использовать любой текстовый файл в качестве входных данных для вашей программы, и он не будет работать на FromBase64 при нормальной работе с ASCIIEncoding.

Теперь для объяснения о хите производительности:

  • ASCIIEncoding (). GetBytes просто берет один символ из вашего файла и преобразует его в байт (что довольно просто: делать нечего). Например, он переведет «A» в 0x41 и «Z» в 0x5A ...

  • Для Convert.FromBase64String это другая история. Это действительно перевод "base64 закодированной строки" в массив байтов. Строка base64 - это, скажем, «печатаемое представление двоичных данных». Лучше всего это «текстовое» представление двоичных данных, которое позволяет, например, отправлять их по Интернет-проводу. Изображения в письмах кодируются с помощью base64, поскольку почтовые протоколы основаны на тексте, поэтому процесс преобразования туда и обратно base64 в байты совсем не прост, поэтому производительность снижается.

Кстати, строка base64 должна выглядеть примерно так:

SABlAGwAbABvAHcAIABXAG8AcgBsAGQAIQA =

что означает "Привет, мир!" не сразу, не так ли?

Вот некоторая информация о формате base64: http://en.wikipedia.org/wiki/Base64

Надеюсь, это поможет

1 голос
/ 15 мая 2009

Я дал несколько советов к другому вопросу, проверьте эти инструменты и ссылки из MS Research.

Они помогут вам устранить любые потенциальные проблемы ввода-вывода или, по крайней мере, понять их.

Кроме того, вы должны следить за проблемами, связанными с кучей объектов CLR LARGE . Перкулярно при использовании массивов (все, что превышает ~ 80 КБ, имеет неоптимальные взаимодействия управляемой кучи, если вы выполняли это 5000 раз в одном и том же процессе).

Однако, действительно, посмотрев еще раз, я не думаю, что они тесно связаны с вашей леммой. Я запустил этот код в профилировщике, и он просто показывает, что Convert. Base64 потребляет все ваши циклы.

Некоторые другие вещи, в вашем тестовом коде, вы всегда должны запускать свой тест 2+ раза подряд, джиттер будет иметь шанс повлиять на нагрузку во время выполнения. Это может вызвать такое изменение во времени выполнения, это удивительно. Прямо сейчас я думаю, что вы должны пересмотреть свою тестовую систему, пытаясь учесть джиттер и возможные эффекты кучи больших объектов. (Поместите одну из этих процедур перед другой ...).

1 голос
/ 15 мая 2009

Для начала, DateTime имеет низкое разрешение (около 0,018 с). Так что лучше используйте класс секундомера.

Теперь это не полностью объясняет разницу, но вы могли бы получить лучшие цифры.

0 голосов
/ 15 мая 2009

Возможно, вы захотите взглянуть на серию статей (и сопутствующий исходный проект), которые недавно написал Джон Скит по этому вопросу

здесь и здесь

В частности, он сравнивал буферизацию с потоковой передачей, но были и интересные результаты с различными размерами файлов и числом потоков.

...