Разработчик ast-grep переписал ядро Tree-sitter с C на Rust, и код для этого писал ChatGPT. Парсинг ускорился почти на 30%, обход готового дерева — на 10%, а полный цикл ast-grep (разбор файла и извлечение структуры) стал потреблять на 22,2% меньше процессорного времени. Платой за скорость стала память: в тестах ast-grep пиковое потребление подросло с 26,52 до 34,43 МиБ, хотя на огромном корпусе TypeScript-файлов удалось ужать его с 1,04 ГиБ до 91,2 МиБ.
Всё началось с того, что любое серьёзное расследование производительности ast-grep упиралось в Tree-sitter — фреймворк, превращающий исходники в синтаксическое дерево. Мечта переписать или глубоко оптимизировать его годами разбивалась о зрелый C-код с инкрементальным парсингом, внешними сканерами и огромной экосистемой грамматик. Затем появление AI-ассистентов вроде тех, что использовали Bun, pgrust и Roc, сделало эксперимент достаточно дешёвым, и автор дал ChatGPT задание перевести рантайм на Rust.
Первый этап — строгий перевод с C, сохраняющий все наблюдаемые свойства. Существующие тесты служили оракулом, сгенерированные грамматики и внешние сканеры должны были работать без изменений, а бинарный интерфейс (ABI) остался нетронутым. ChatGPT перелопатил код по частям, сверяясь с тестами, и на выходе получился работающий Rust-рантайм, совместимый со всей экосистемой.
Сразу после этого разработчик попросил агента ускорить парсинг на 20%. Результат появился, но код превратился в нечитаемое месиво из наложенных друг на друга оптимизаций, а парсер начал падать без внятных ошибок. Всю оптимизацию откатили. Стало ясно: вместо того чтобы гнаться за цифрами, нужно сделать систему объяснимой.
Начали с сокращения scope. Поскольку целевая нагрузка — инструменты для агентного кодинга, анализирующие снимки файлов целиком, а не редактор с покадровыми правками, вырезали инкрементальный парсинг и нативную загрузку Wasm-грамматик. Публичные параметры остались для совместимости, но рантайм теперь всегда парсит файл с нуля. Затем почистили сам код: внутренние сырые указатели заменили ссылками и Option, разбили монолитные модули, а неустранимые трюки вроде компактных индексов упрятали за узкими именованными операциями. Получился код, в котором можно было отследить владение и понять, почему одно действие плодит временные состояния.
Следом ChatGPT занялся алгоритмическими оптимизациями под контролем разработчика. В GLR-парсере 99% времени стек остаётся линейным, поэтому обобщённый граф теперь строится только при реальной неоднозначности. Внутренние узлы дерева выделяются из арены, а не у общего аллокатора. Компактные индексы ускорили обмен данными между стеком и деревом, а для тривиальных случаев вроде обычного ASCII сделали быстрые пути в обход полной обработки символов.
Но когда ast-grep собрали с новым ядром и запустили на реальном репозитории opencode, приложение неожиданно замедлилось. Выяснилось, что при создании парсера для каждого файла арена резервировала огромный блок виртуальной памяти, и тысячи таких резервирований создавали тяжёлую нагрузку на syscall-ы и страничные ошибки. Заменили резервирование обычным маленьким выделением с ростом по необходимости — это закрыло регрессию. Тут же вылезла проблема с памятью: на стресс-тесте пик взлетел выше гигабайта. Потребовалось несколько итераций доработки арены, чтобы срезать его до 91,2 МиБ. Финальным штрихом стала оптимизация чтения дерева: ast-grep научился запрашивать дочерние узлы группы один раз, а не дёргать их при каждом обходе.
Итоговая победа родилась не из одной гениальной заплатки, а из последовательной чистки, профилирования и прицельных правок. ChatGPT дал возможность запустить переписывание, но без человека, который сужал вопросы, проверял доказательства и отлавливал сегфолты за красивыми цифрами, ничего бы не вышло.