Как эффективно писать большой текстовый файл в С#?
Я создаю метод в С#, который генерирует текстовый файл для Google Product Feed. Канал будет содержать более 30 000 записей, а текстовый файл в настоящий момент весит ~ 7 МБ.
Вот код, который я использую в настоящее время (некоторые строки удалены для краткости).
public static void GenerateTextFile(string filePath) {
var sb = new StringBuilder(1000);
sb.Append("availability").Append("\t");
sb.Append("condition").Append("\t");
sb.Append("description").Append("\t");
// repetitive code hidden for brevity ...
sb.Append(Environment.NewLine);
var items = inventoryRepo.GetItemsForSale();
foreach (var p in items) {
sb.Append("in stock").Append("\t");
sb.Append("used").Append("\t");
sb.Append(p.Description).Append("\t");
// repetitive code hidden for brevity ...
sb.AppendLine();
}
using (StreamWriter outfile = new StreamWriter(filePath)) {
result.Append("Writing text file to disk.").AppendLine();
outfile.Write(sb.ToString());
}
}
Мне интересно, является ли StringBuilder правильным инструментом для работы. Будет ли выигрыш в производительности, если я вместо этого использовал TextWriter?
Я не знаю тонкости производительности IO, поэтому любая помощь или общие улучшения будут оценены. Спасибо.
Ответы
Ответ 1
Операции ввода-вывода файлов обычно хорошо оптимизированы в современных операционных системах. Вам не следует пытаться собрать всю строку для файла в памяти... просто напишите его по частям. FileStream
позаботится о буферизации и других соображениях производительности.
Вы можете легко сделать это изменение, перемещая:
using (StreamWriter outfile = new StreamWriter(filePath)) {
в начало функции и избавиться от StringBuilder
, записывая непосредственно в файл.
Есть несколько причин, по которым вам следует избегать создания больших строк в памяти:
- Это может действительно ухудшиться, потому что
StringBuilder
должен увеличить свою емкость при записи на него, что приведет к перераспределению и копированию памяти.
- Это может потребовать больше памяти, чем вы можете физически выделить, что может привести к использованию виртуальной памяти (файла подкачки), которая намного медленнее, чем оперативная память.
- Для действительно больших файлов ( > 2Gb) у вас закончится адресное пространство (на 32-разрядных платформах) и не будет завершено.
- Чтобы записать содержимое
StringBuilder
в файл, вы должны использовать ToString()
, что эффективно удваивает потребление памяти процессом, поскольку обе копии должны находиться в памяти в течение определенного периода времени. Эта операция может также завершиться неудачно, если ваше адресное пространство достаточно фрагментировано, так что нельзя выделить один непрерывный блок памяти.
Ответ 2
Просто переместите оператор using
, чтобы он охватывал весь ваш код и записывался непосредственно в файл. Я не вижу смысла держать все в памяти в первую очередь.
Ответ 3
Напишите одну строку за раз, используя StreamWriter.Write, а не кеширование всего в StringBuilder.