Часто при работе с внешними данными в формате CSV разработчики 1С сталкиваются с проблемой катастрофической потери производительности. В процессе анализа подобных задач мы часто видим одну и ту же картину: отчет, который на малых объемах работает приемлемо, при получении файла размером 50 Мб и более начинает выполняться по 10–20 минут. Давайте разберем, где кроются узкие места и как правильно настроить процесс импорта данных.
Первое, с чего начинается оптимизация — выбор инструмента. Использование объекта ТекстовыйДокумент оправдано только для мелких файлов конфигурации или коротких логов. Для больших массивов данных он становится «бутылочным горлышком», так как вынужден полностью загружать файл в оперативную память. Мы настоятельно рекомендуем переходить на ЧтениеТекста.
ЧтениеТекста — это потоковый объект, который позволяет читать файл последовательно, не перегружая серверную память. При работе с ним потребление ресурсов становится линейным относительно объема файла.
Рассмотрим пример инициализации правильного чтения:
Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.ANSI);
СтрокаДанных = Чтение.ПрочитатьСтроку();
Пока СтрокаДанных <> Неопределено Цикл
// Обработка данных
СтрокаДанных = Чтение.ПрочитатьСтроку();
КонецЦикла;
Основная проблема производительности, как показал анализ, заключалась не в самом чтении, а в логике обработки каждой строки. Многие разработчики допускают ошибку, пытаясь «на лету» определять тип колонки или приводить данные к нужному формату через функции поиска подстрок внутри каждого цикла.
Представьте: в файле 300 000 строк и 50 колонок. Если внутри цикла вы вызываете функцию поиска (например, через Найти()) или функцию получения описания типов (ПолучитьОписаниеТиповЧисла()), 1С выполняет миллионы лишних операций за один сеанс работы отчета. Это и создает те самые «зависания» на 20 минут.
Чтобы ускорить процесс, необходимо вынести всю подготовительную работу за пределы цикла. Сначала проанализируйте структуру CSV, определите типы данных для колонок один раз и сохраните их в структуру или массив.
Рассмотрим, как выглядит неправильный (медленный) подход и как его исправить.
// В этой функции поиск и получение типов вызываются
// для каждой ячейки каждой строки
Функция ПривестиЗначениекТипу(Значение, ИмяКолонки)
ТипКолонки = УстановитьТипКолонки(ИмяКолонки); // Дорогостоящая операция!
// Дальнейшие преобразования...
КонецФункции
Разберем по шагам, как нужно перестроить работу:
Соответствие, где ключом будет имя колонки, а значением — ОписаниеТипов.Соответствие, чтобы мгновенно получить нужный тип.
// Заранее подготовленное соответствие типов
СтруктураТипов = ПолучитьСтруктуруТиповДляКолонок();
// В цикле обработки строк:
Для Каждого СтрокаДанных Из МассивСтрок Цикл
Для НомерКолонки = 0 По МассивКолонок.Количество() - 1 Цикл
ТипДляКолонки = СтруктураТипов[ИмяКолонки];
// Прямое преобразование без лишних поисков
Значение = ПривестиКТипу(МассивКолонок[НомерКолонки], ТипДляКолонки);
КонецЦикла;
КонецЦикла;
Кроме типизации, обратите внимание на следующие моменты:
Сред() и Дата() для создания даты вручную, вместо попыток сложного парсинга.Таким образом, основной секрет высокой скорости заключается в минимизации логики внутри циклов. Сделав замер производительности, вы обнаружите, что 60-80% времени уходит на повторяющиеся вызовы одних и тех же функций. Убрав их в начало алгоритма, вы кратно увеличите скорость формирования отчета.