Небуферизованный вывод очень медленный
Я генерирую очень большой файл.csv из базы данных, используя метод, изложенный в
/questions/22769912/zapis-v-potok-vyivoda-iz-dejstviya/22769947#22769947
Работает нормально, до определенного момента. Когда экспортируемый файл слишком велик, я получаю OutOfMemoryException
,
Если я отключу буферизацию вывода, изменив этот код следующим образом:
protected override void WriteFile(System.Web.HttpResponseBase response)
{
response.BufferOutput = false; // <--- Added this
this.Content(response.OutputStream);
}
загрузка файла завершена. Однако он на несколько порядков медленнее, чем при включенной буферизации вывода (измерено для того же файла с буферизацией true / false на локальном хосте).
Я понимаю, что это медленнее, но почему это замедлится до относительного сканирования? Что я могу сделать, чтобы улучшить скорость обработки?
ОБНОВИТЬ
Также будет предложено использовать File(Stream stream, String contentType), как это предлагается в комментариях. Тем не менее, я не уверен, как создать stream
, Данные динамически собираются на основе запроса к БД, и MemoryStream исчерпывает непрерывную физическую память. Предложения приветствуются.
ОБНОВЛЕНИЕ 2
В комментариях было высказано предположение, что поочередное чтение из базы данных и запись в поток приводит к ухудшению. Я изменил код для выполнения записи потока в отдельном потоке (используя шаблон производитель / потребитель). Там нет заметной разницы в производительности.
2 ответа
Я не знаю, что именно ASP.NET и IIS делают с потоковой передачей, но, возможно, используются слишком маленькие куски. Крюк в BufferedStream
с очень большим буфером, как 4 МБ.
По вашим комментариям это сработало. Теперь уменьшите размер буфера, чтобы сэкономить память и иметь меньший рабочий набор. Хорошо для кеша.
В качестве субъективного комментария я разочарован тем, что это даже необходимо. IIS должен использовать правильные буферы автоматически, что очень просто для TCP-соединений.
РЕДАКТИРОВАТЬ ИЗ ОП
Вот код, полученный из этого ответа
public ActionResult Export()
{
// Domain specific stuff here
return new FileGeneratingResult("MyFile.txt", "text/text",
stream => this.StreamExport(stream), false);
}
private void StreamExport(Stream stream)
{
using (BufferedStream bs = new BufferedStream(stream, 256*1024))
using (StreamWriter sw = new StreamWriter(bs))
foreach (var stuff in MyData())
{
sw.Write(stuff);
}
}
В последнем обновлении Эрика он упомянул использование другого потока. У меня тоже была эта проблема для реализации экспорта базы данных. Вот пример кода для решения, которое я использовал: