Верстакфорум практиков
рекламаiprazon: приватные серверные адреса IPv4 и SOCKS5, безлимитный трафик, бесплатный тест до 2 часов
ФорумСофт и программы

Сравнил три парсера по памяти на одном задании

nullbyte
nullbyte
Ветеран
сообщений 2680
с апр
22 апреля, 17:27первое сообщение

Взял одно задание: 400 тысяч адресов, разбор заголовка и двух полей, вывод в csv. Прогнал через три инструмента подряд на одной машине, мерил пиковую занятую память.

Условия одинаковые: 30 соединений, тот же список, тот же диск. Разница вышла заметнее, чем я ждал.

Пик занятой памяти на одном задании, мегабайты
Пик занятой памяти на одном задании, мегабайты

Node меня удивил. Скрипт короткий, лишнего в памяти не держит, память всё равно ползёт вверх весь прогон. Кто копал в эту сторону?

если не воспроизводится, значит не понял
Пётр Мельник
Пётр Мельник
Участник
сообщений 810
с июл
1 сентября, 08:44#2

Сразу вопрос по методике: чем мерили и что считали пиком.

сначала замер, потом мнение
nullbyte
nullbyte
Ветеран
сообщений 2680
с апр
8 февраля, 11:01#3

Мерил через /usr/bin/time -v, брал строку Maximum resident set size. Сверху раз в минуту смотрел pmap, чтобы понять, растёт ровно или ступеньками.

замер по одному прогону
замер по одному прогону

Ступеньками, кстати. Каждые минут двадцать плюс полторы сотни мегабайт, вниз не возвращается.

если не воспроизводится, значит не понял
Борис Лапин
Борис Лапин
Знаток
сообщений 1960
с мар
15 июля, 14:18#4

Ступеньки без отката это почти всегда накопление, течь в самом движке тут ни при чём.

Три места, где в Node оно вылезает чаще всего.

Первое: результат копится в массиве и пишется в файл одним куском в конце. Пока прогон идёт, весь разобранный csv лежит в памяти. На 400 тысячах строк по 300 байт это уже больше сотни мегабайт, и сверху накладные расходы на объекты, а они у V8 немаленькие.

Второе: буферы ответов. Если вы собираете тело через body += chunk, каждая склейка порождает новую строку. Сборщик их подберёт, но с задержкой, и пик по памяти вы уже увидели.

Третье: очередь заданий целиком в памяти. Прочитали файл со списком через readFileSync и split, получили массив на 400 тысяч строк. Он живёт до конца прогона, потому что на него ссылается цикл.

У Scrapy память ниже ровно потому, что очередь по умолчанию уезжает на диск, а строки отдаются генератором по одной.

veta_l
veta_l
Новичок
сообщений 210
с янв, второй сезон
22 декабря, 17:35#5

а генератор это что, можно на пальцах

Борис Лапин
Борис Лапин
Знаток
сообщений 1960
с мар
1 мая, 08:52#6

Функция, которая отдаёт строки по одной по запросу, вместо того чтобы вернуть сразу весь список.

const fs = require('fs'), readline = require('readline');
async function* stroki(put) {
  const rl = readline.createInterface({ input: fs.createReadStream(put), crlfDelay: Infinity });
  for await (const s of rl) if (s.trim()) yield s.trim();
}

Файл читается кусками, в памяти висит одна строка плюс буфер потока. Замените readFileSync на это, и ступеньки станут заметно ниже.

rawsocket
rawsocket
Ветеран
сообщений 2290
с фев
8 октября, 11:09#7

Добавлю сбоку про запись. Открывайте поток на вывод один раз и пишите по мере разбора, с оглядкой на drain. Тогда массив с результатами вам вообще не нужен, память перестаёт зависеть от длины списка.

И про машину. Такие замеры на домашней тачке врут: там и своп другой, и сеть неровная. Длинные прогоны я увожу на арендованный сервер, беру приватные серверные адреса для парсинга с безлимитным трафиком и мерю уже там. Цифры получаются повторяемые.

читаю байты, а потом уже документацию
nullbyte
nullbyte
Ветеран
сообщений 2680
с апр
15 марта, 14:26#8
Борис Лапин: очередь заданий целиком в памяти

Вот оно. У меня именно readFileSync и split на старте. Переделываю.

если не воспроизводится, значит не понял
batchrunner
batchrunner
Участник
сообщений 470
с окт
22 августа, 17:43#9

Перемерил у себя на похожем задании после правок, оставлю цифры для сравнения.

ИнструментПик памятиЧто менял
A-Parser590 МБничего, коробка
Scrapy410 МБничего, коробка
Node, было1150 МБсписок и результат в памяти
Node, стало190 МБчтение построчно, запись потоком

Разница в шесть раз на ровном месте.

Пётр Мельник
Пётр Мельник
Участник
сообщений 810
с июл
1 января, 08:00#10

Прошу учесть при чтении темы: пик у A-Parser зависит от того, сколько результатов вы держите в наборе до выгрузки. Поставьте выгрузку каждые десять тысяч строк, и его цифра тоже просядет. Сравнение честное только при одинаковых настройках выгрузки.

сначала замер, потом мнение
nullbyte
nullbyte
Ветеран
сообщений 2680
с апр
8 июня, 11:17#11

Переписал чтение на построчное, запись на поток, массив выкинул совсем. Пик 205 мегабайт, прогон стал даже чуть быстрее, потому что сборщик перестал молотить вхолостую.

Закрываю. Итог для тех, кто найдёт поиском: память ела не библиотека, ела моя привычка держать весь список рядом.

если не воспроизводится, значит не понял