Почему использование буферизует к Потокам чтения-записи

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

private void CopyStream(Stream input, Stream output)
{
   byte[] buffer = new byte[16 * 1024];
   int read;
   while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
   {
      output.Write(buffer, 0, read);
   } 
}

Два вопроса:

Почему считанный и запись в этих меньших блоках?

Каково значение используемого размера буфера?

10
задан James Hay 12 May 2010 в 17:57
поделиться

2 ответа

Если вы читаете байт за раз, то каждый вызываемый вами байт имеет накладные расходы на вызов функции для чтения байта и дополнительные накладные расходы (например, , выполняя fileposition + = 1 , чтобы запомнить, где вы находитесь в файле, проверяя, достигли ли вы конца файла и т. д.)

Если вы читаете 4000 байт, то у вас есть те же накладные расходы (в приведенном выше примере 1 вызов функции, одно добавление (fileposition + = 4000) и одна проверка, чтобы увидеть, находитесь ли вы в конце файла. Итак, с точки зрения накладных расходов, вы только что сделали 4000 раз быстрее (на самом деле, есть и другие затраты, поэтому вы не увидите такой большой выгоды, но вы резко сократили накладные расходы)

Конечно, вы можете создать буфер размером с весь файл и получить абсолютный минимум накладных расходов.Однако:

  • файл может быть огромным - больше, чем память, доступная вашей программе, так что это просто не удастся.Или он может быть настолько большим, что вы начнете использовать виртуальную память, и это резко замедлит работу. Таким образом, разбиение его на более мелкие куски означает, что вы можете копировать неограниченное количество данных с помощью небольшого буфера фиксированного размера

  • , ваша ОС и устройства могут читать и записывать данные одновременно (например, копирование с одного физического диска на другой) . Если вы читаете все данные до того, как записываете все данные, вам придется дождаться всего чтения, прежде чем вы сможете начать запись. Но во многих случаях вы можете выполнять обе операции параллельно - поэтому прочтите небольшой фрагмент и начните его писать «асинхронно» (в фоновом режиме), пока вы возвращаетесь и читаете следующий фрагмент.

  • Вы получаете убывающую прибыль. Чтение 4 байтов вместо 1 вполне может быть в 4 раза быстрее. Но чтение 4,000, 40,000 или 400,000 не ускорит работу (действительно, по причинам, указанным выше, большие буферы могут действительно замедлить работу).

  • В некоторых случаях физические устройства работают с данными определенного размера (например, 4096 байтов на сектор, 128 байтов на строку кэша или 1500 байтов на пакет данных, или 8 байтов (64 бита) по шине ЦП). Разделение данных на блоки, которые соответствуют (или кратны) базовому механизму транспортировки / хранения, может помочь оборудованию более эффективно обрабатывать данные.

Обычно буферы ввода-вывода размером от 4 до 128 КБ лучше всего подходят для большинства ситуаций, и вы можете настроить их для конкретной выполняемой операции, поэтому не существует «идеального» размера, который подходил бы для всех ситуаций.

Обратите внимание, что в большинстве ситуаций ввода-вывода используется много буферов. напримерПри копировании данных с диска (упрощенно) они считываются с диска в кэш чтения (буфер) на жестком диске, а затем отправляются по интерфейсному кабелю на контроллер диска компьютера, который также может буферизовать данные. Затем он может быть перенесен в ОЗУ через буфер ввода-вывода, где он хранится до тех пор, пока ваша программа не будет готова принять его (вероятно, она даже будет получать данные до того, как вы их запросите, поскольку ожидает, что вы продолжите чтение из тот же файл и пытается буферизовать данные, чтобы вам не приходилось ждать этого). Затем вы читаете его в свой буфер и записываете. Затем он переходит в другой буфер ввода-вывода, отправляется контроллеру накопителя, передается на накопитель и кэшируется в кэше записи. В конце концов, жесткий диск решит фактически сохранить данные в своем кэше записи, и ваша копия будет завершена - большая часть этого происходит в фоновом режиме, поэтому запись может закончиться только через несколько секунд после того, как ваша программа решит, что она закончила запись и перешел к другой задаче. (Вот почему вы должны «безопасно извлечь» USB-накопители перед их отключением - возможно, ОС еще не записала все данные на устройство, даже через много секунд после того, как компьютер сообщил, что ваша операция копирования завершена)

{{ 1}}
6
ответ дан 4 December 2019 в 01:29
поделиться

Обычно вы всегда можете выбрать размер для чтения и записи. Однако некоторые значения будут более оптимальными для конкретных архитектур. Что это такое, мне не известно. Я всегда был склонен придерживаться привычных мне цифр, таких как 4K (размер страницы в NT-системах, для которых я использовал драйверы записи). Но я экспериментировал в пользовательском режиме с большими размерами и никогда не сталкивался с какими-либо проблемами. Я стараюсь минимизировать количество вызовов ввода-вывода.

Я предлагаю, чтобы в наши дни размер блока действительно важен только в том случае, если он очень маленький (накладные расходы на операцию по сравнению с полученным объемом) или очень большой (блокировка и насыщение системы ввода-вывода).

Я думаю, что в любом конкретном случае вам следует

  1. Минимизировать количество вызовов ввода-вывода
  2. Измените эту стратегию, если реальная производительность является проблемой.
4
ответ дан 4 December 2019 в 01:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: