Почему Stream.Copy быстрее, чем Stream.Write в FileStream? - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть вопрос, и я не могу найти причину для этого.Я создаю собственный файл архива.Я использую MemoryStream для хранения данных и, наконец, я использую FileStream для записи данных на диск.

Мой жесткий диск SSD , но скорость была слишком медленной,Когда я пытался записать в файл только 95 МБ, потребовалось 12 секунд для записи!

Я попытался Filestream.Write и File.WriteAllBytes, но это одно и то же.

В конце у меня появилась идея сделать это с копированием, и это было 100x быстрее!

Мне нужно знать, почему это происходит и что не так с функциями записи.

Вот мой код:

//// First of all I create an example 150MB file
Random randomgen = new Random();
byte[] new_byte_array = new byte[150000000];
randomgen.NextBytes(new_byte_array);

//// I turned the byte array into a MemoryStream
MemoryStream file1 = new MemoryStream(new_byte_array);
//// HERE I DO SOME THINGS WITH THE MEMORYSTREAM


/// Method 1 : File.WriteAllBytes | 13,944 ms
byte[] output = file1.ToArray();
File.WriteAllBytes("output.test", output);

// Method 2 : FileStream | 8,471 ms
byte[] output = file1.ToArray();
FileStream outfile = new FileStream("outputfile",FileMode.Create,FileAccess.ReadWrite);
outfile.Write(output,0, output.Length);

// Method 3 | FileStream | 147 ms !!!! :|
FileStream outfile = new FileStream("outputfile",FileMode.Create,FileAccess.ReadWrite);
file1.CopyTo(outfile);

Кроме того, file1.ToArray() требуется всего 90 мс для преобразования MemoryStream в байты.

Почему это происходит и чтов чем причина и логика?

1 Ответ

0 голосов
/ 11 февраля 2019

Обновление

Дмитрий Мукалов имеет право.Исполнения, которые вы получаете, расширяя внутренний буфер FileStream, будут убраны, когда вы выполните Flush.Я копаю немного глубже и сделал некоторый тест, и кажется, что разница между Stream.CopyTo и FileStream.Write заключается в том, что Stream.CopyTo использует более умный буфер ввода-вывода и повышает производительность, копируя чанк по чанку.В конце CopyTo используйте Write под капотом.Оптимальный размер буфера обсуждался здесь .

Оптимальный размер буфера связан с рядом факторов: размером блока файловой системы, размером кэша ЦП и задержкой кэша.Большинство файловых систем сконфигурировано для использования блоков размером 4096 или 8192. Теоретически, если вы конфигурируете размер буфера таким образом, что вы читаете на несколько байтов больше, чем дисковый блок, операции с файловой системой могут быть крайне неэффективными (т.е. если высконфигурировал ваш буфер для чтения 4100 байт за раз, при каждом чтении файловая система требовала 2 чтения блока).Если блоки уже находятся в кеше, вы платите цену RAM -> L3 / L2 задержка кеша.Если вам не повезло, а блоки еще не в кеше, вы также платите цену за задержку диска-> ОЗУ.

Поэтому, чтобы ответить на ваш вопрос, в вашем случае вы используете неоптимизированный буферразмеры при использовании Write и оптимизированные при использовании CopyTo или, лучше сказать, Stream само по себе оптимизирует это для вас.

Как правило, вы также можете принудительно использовать неоптимизированный CopyTo, расширяя FileStreamвнутренний буфер, в этом случае результаты должны быть сравнительно медленными, так как неоптимизированные Write.

FileStream outfile = new FileStream("outputfile",
    FileMode.Create, 
    FileAccess.ReadWrite,
    FileShare.Read,
    150000000); //internal buffer will lead to inefficient disk write
file1.CopyTo(outfile);
outfile.Flush(); //don't forget to flush data to disk

Оригинал

Я сделал анализ Write методовFileStream и MemoryStream, и дело в том, что MemoryStream всегда использует внутренний буфер для копирования данных, и это очень быстро.У FileStream есть переключатель, если запрошено count >= bufferSize, что верно в вашем случае, так как вы используете буфер по умолчанию FileStream, размер буфера по умолчанию равен 4096.В этом случае FileStream вообще не использует буфер, кроме собственного Win32Native.WriteFile.

Хитрость заключается в том, чтобы заставить FileStream использовать буфер, переопределив размер буфера по умолчанию.Попробуйте это:

// Method 2 : FileStream | 8,471 ms
byte[] output = file1.ToArray();
FileStream outfile = new FileStream("outputfile",
    FileMode.Create,
    FileAccess.ReadWrite, 
    FileShare.Read,
    output.Length + 1); // important, the size of the buffer
outfile.Write(output, 0, output.Length);

nb Я не говорю, что это оптимальный размер буфера, просто объяснение того, что происходит.Чтобы определить наилучший размер буфера с помощью FileStream, обратитесь к link .

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