Как один линт Clippy ускорили в 3133 раза

· Программы
Разработчик Rust-линтера Clippy рассказал, как безобидная с виду проверка макросов съедала четверть времени работы инструмента — и как её удалось радикально ускорить.

Линт clippy::nonstandard_macro_braces следит за тем, чтобы макросы вызывались с правильными скобками. В Rust пустой вектор создают через vec![], но формально ничто не мешает написать vec!() или даже vec! {"???"}. Такой код раздражает всех, кто его читает, поэтому Clippy для ряда известных макросов хранит «канонический» вид скобок и подсказывает разработчику, когда тот отступает от нормы.

Почему это было медленно

Проблема в том, что Clippy работает уже после раскрытия макросов. Вместо println! {"..."} он видит развёрнутый код вроде std::io::_print(std::format_args_nl!("Hello, world!")), и по нему невозможно понять, какие скобки стоял в исходнике. Rust не хранит явной карты вызовов макросов — по признанию автора, это одна из главных болей Clippy.

Чтобы восстановить исходные скобки, линт поднимался вверх по цепочке раскрытия, обращался к «глобальным данным сессии» с информацией о гигиене макросов, доставал по спану исходный текст и уже строковыми манипуляциями (разбить по !, обрезать, посмотреть на первый символ) выяснял, что за скобка использовалась.

Звучит терпимо — пока не выясняется, что вся эта процедура запускалась для каждого выражения, оператора и элемента во всём проекте. Каждый раз она дважды обращалась к данным гигиены, блокировала интернер символов и запускала рекурсию. То есть фактически тормозила весь компилятор ради проверки, которая в итоге отъедала около 25% времени работы Clippy.

«Виноваты сопровождающие»

Автор специально подчёркивает: это не ошибка конкретного контрибьютора и винить его неправильно. Ответственность лежит на мейнтейнерах, чья задача — сделать написание плохого кода максимально трудным. Rust избавляет от переполнений буфера и use-after-free, но это иногда означает, что придётся писать больше кода, реже прибегать к макросам и не увлекаться абстракциями настолько, чтобы «сотню раз виденная» функция незаметно съедала четверть времени работы.

Решение

Исправление уложилось менее чем в 200 строк: проблемную функцию переписали из пост-раскрывающего линта в пре-раскрывающий. Больше не нужно доставать исходный текст и вычислять спан скобок арифметикой, не нужно блокировать ни интернер символов, ни глобальные данные сессии. Автор честно признаёт, что работа с кодом до раскрытия макросов в Clippy считается хаком, которому не стоит доверять, — но этот хак экономит сотни тысяч «вычислительных долларов».

В довершение появился и давно задуманный сервер для бенчмарков — самостоятельно размещённый, с памятью на 200 дней и на распространённой архитектуре процессора, так что цифры близки к реальному пользовательскому опыту. Построить его удалось благодаря контракту с Rust Foundation.


Источник: Lobsters

Комментарии

Войдите, чтобы комментировать.

  • Пока нет комментариев.