BookinglyTech News
Software

El lint de Clippy que se comía el 25% del tiempo ahora es 3133 veces más rápido

clippy::nonstandard_macro_braces consumía en torno al 25% del tiempo de ejecución de Clippy. Su autor lo ha reescrito en menos de 200 líneas y la mejora que reporta es de 3133 veces.

2 min de lecturaLobsters0 vistas

El lint clippy::nonstandard_macro_braces se llevaba en torno al 25% del tiempo de ejecución de Clippy. Su autor lo ha reescrito y ahora esa función va 3133 veces más rápido, según sus propias medidas. El cambio está en el repositorio: menos de 200 líneas.

El lint hace algo simple: avisar cuando llamas a una macro con las llaves que no toca. En Rust, vec![] es lo correcto; vec!() o vec!{"?"} no. Nadie escribe println!{} sin que le miren mal. El problema estaba en cómo se comprobaba.

Clippy se ejecuta después de la expansión de macros. Eso significa que no ve println!{"..."}; ve std::io::_print(std::format_args_nl!("Hello, world!"));. Las llaves originales no están ahí. Para recuperarlas, el lint subía por la cadena de expansión, consultaba los datos de higiene en los session globals, sacaba el texto fuente del span correspondiente y hacía manipulación de cadenas para deducir si el delimitador era [, ( o {.

Todo eso, además, bloqueando el símbolo interner y los session globals, dos recursos compartidos que paran el resto del compilador mientras están tomados. Y lo hacía para cada expresión, sentencia e ítem del código del usuario.

De post-expansión a pre-expansión

La solución fue reescribir la función como un lint de pre-expansión. Se acabaron el texto fuente, las cuentas con el span y los bloqueos. El autor lo llama un hack, y no le falta razón: el código pre-expansión no es de fiar, puede engañar. Pero es un hack que, dice, ahorra cientos de miles de dólares en cómputo.

En el texto no hay reproche para quien escribió el código original. Lo deja claro: la culpa no es del contribuyente, es de los mantenedores, que deberían poner difícil escribir código así. Rust ya quita de encima los desbordamientos de buffer y el use-after-free; lo que queda es no abstraerse hasta la locura y montar una función aparentemente inocua que se come un cuarto del tiempo de ejecución.

Un servidor de benchmarks

Clippy tiene desde ahora un servidor de benchmarking. El autor llevaba unos cuatro años detrás de montarlo y lo ha sacado adelante con un contrato de la Rust Foundation. Guarda 200 días de memoria, es self-hosted hasta cierto punto y ha hecho las medidas en hardware doméstico, de arquitectura común, así que los números deberían parecerse a lo que ve un usuario real.

El pull request #16808 está abierto en el repositorio de rust-clippy. Queda por ver cuánto tarda en llegar a stable, pero el ahorro de CPU es real desde que se mergee.