При разработке механизмов интеграции или выгрузки данных программисты часто сталкиваются с проблемой: запись небольших объемов информации происходит мгновенно, но как только размер файла превышает несколько мегабайт, время выполнения операции начинает расти в геометрической прогрессии. В этой статье мы подробно разберем, почему стандартные средства могут работать медленно, и проанализируем наиболее эффективные способы ускорения записи в текстовые файлы.
Рассмотрим стандартный подход, который часто используют начинающие разработчики: создание объекта ТекстовыйДокумент и последовательное использование метода ДобавитьСтроку(). Проанализируем ситуацию: при записи данных из ТаблицыЗначений в один файл объемом около 6 Мб процедура может занимать десятки минут.
Причина низкой производительности заключается в архитектуре объекта ТекстовыйДокумент. Он изначально проектировался для интерактивной работы в интерфейсе (отображение текста пользователю, редактирование). При каждом вызове метода ДобавитьСтроку() система выполняет пересчет индексов строк и перераспределение памяти, чтобы поддерживать актуальное состояние документа для отображения. Чем больше строк уже добавлено в документ, тем медленнее работает добавление каждой последующей строки. Поэтому запись одного большого файла оказывается значительно медленнее, чем запись тех же данных, разбитых на десятки мелких файлов.
Выясним, как можно обойти ограничения объектной модели. Один из самых быстрых способов в 1С — это предварительный сбор данных в Массив с последующим объединением в одну гигантскую строку. Рассмотрим этот алгоритм по шагам:
Массив для временного хранения строк.СтрСоединить(), чтобы собрать все элементы массива в одну строку, разделенную символами переноса строки.ЗаписьТекста.Посмотрим на пример кода, реализующий этот подход:
// Подготовим массив для данных
Мас = Новый Массив;
Для каждого СтрТЗ из ТаблицаДанных Цикл
// Формируем строку (например, для CSV)
ТекущаяСтрока = Формат(СтрТЗ.КМ, "ЧГ=0") + ";" +
Формат(СтрТЗ.КТ, "ЧГ=0") + ";" +
Формат(СтрТЗ.ОТ, "ЧГ=0");
Мас.Добавить(ТекущаяСтрока);
КонецЦикла;
// Объединяем массив в одну строку и записываем в файл
ФайлТЗ = Новый ЗаписьТекста;
ФайлТЗ.Открыть(ИмяФайла, "CESU-8");
ФайлТЗ.Записать(СтрСоединить(Мас, Символы.ПС));
ФайлТЗ.Закрыть();
Важный нюанс: многих разработчиков смущает вопрос ограничения длины строки. В современных версиях платформы 1С (8.3) длина строки ограничена только объемом доступной оперативной памяти (для 64-битных систем) или пределом в 2 Гб (для 32-битных). Для файлов размером в несколько десятков или даже сотен мегабайт этот метод является абсолютно безопасным и чрезвычайно быстрым.
Разберем другой профессиональный подход — потоковый вывод. Вместо формирования огромной строки в памяти, мы можем писать данные напрямую в файл порциями. Для этого используется объект ЗаписьТекста и его метод ЗаписатьСтроку(). Этот объект использует внутреннюю буферизацию: данные накапливаются в небольшом буфере в оперативной памяти и сбрасываются на диск только тогда, когда буфер заполняется.
Проанализируем пример реализации потоковой записи:
Запись = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8);
Для каждого СтрТЗ из ТаблицаДанных Цикл
// Используем СтрШаблон для удобства формирования строки
ТекстСтроки = СтрШаблон("%1;%2;%3",
Формат(СтрТЗ.КМ, "ЧГ=0"),
Формат(СтрТЗ.КТ, "ЧГ=0"),
Формат(СтрТЗ.ОТ, "ЧГ=0"));
Запись.ЗаписатьСтроку(ТекстСтроки);
КонецЦикла;
Запись.Закрыть();
Этот метод почти так же быстр, как и СтрСоединить, но потребляет гораздо меньше оперативной памяти, так как не держит в ней все данные выгрузки одновременно. Это критично при работе с файлами объемом в сотни мегабайт или гигабайты.
Рассмотрим ситуацию, когда даже потоковая запись кажется медленной. Часто узким местом становится не сама запись на диск, а подготовка строковых данных. Функция Формат() является универсальной, но она требует времени на разбор строки маски (например, "ЧГ=0") при каждом вызове.
Выясним, как ускорить этот процесс. Существует функция XMLСтрока(), которая предназначена для приведения примитивных типов к каноническому виду XML. Она работает значительно быстрее, так как не имеет настроек форматирования. Если для вашей выгрузки (например, в формате CSV) допустимо использование точки в качестве разделителя дробной части и отсутствие разделителей групп разрядов, используйте именно её.
Сравним производительность: при выгрузке 100 000 строк использование XMLСтрока() может сэкономить до нескольких секунд чистого времени процессора по сравнению с Формат().
Ограничение: функция XMLСтрока() на тонком клиенте поддерживает только примитивные типы (числа, строки, даты, булево). Для ссылочных типов она доступна только на стороне сервера.
Чтобы добиться максимальной скорости, следует учитывать еще несколько факторов:
Стр.Номенклатура.Наименование). Каждое такое обращение — это отдельный запрос к базе данных или кэшу объектов. Лучше заранее подготовить все необходимые данные в ТаблицеЗначений с помощью одного запроса.ТаблицыЗначений с сервера на клиент для записи файла на локальный диск пользователя создает избыточную нагрузку на сеть и сериализацию данных. Правильнее сформировать файл на сервере, поместить его во временное хранилище и затем передать клиенту одним файлом.ЗаписьXML с методом ЗаписатьБезОбработки(), однако на практике стандартный объект ЗаписьТекста обеспечивает достаточную производительность для 99% задач автоматизации.Подведем итог: для быстрой записи данных забудьте про ТекстовыйДокумент. Используйте ЗаписьТекста в сочетании с СтрСоединить (для средних объемов) или ЗаписатьСтроку (для больших объемов), а также отдавайте предпочтение XMLСтрока там, где это возможно.