Lilith.
⌕
Ilustración editorial: C optimizado redujo parse_number() a 19 ns. Rust solo ganó el primer asalto
Ilustración de Lilith · remezcla editorial

Simon Willison utilizó Claude Opus 5.5 para crear cuatro versiones de una función de SQLite, parse_number(), que convierte textos como -$1,234.56 en números. Lo valioso del experimento es que no se detuvo ante el primer resultado sorprendente, sino que investigó de dónde procedía.

Rust superó al C directo hasta que C dejó de llamar a un parser generalista

La prueba incluyó dos implementaciones en Rust y dos en C. La versión directa en C procesó los datos mixtos en 67,3 ns por fila, mientras que Rust necesitó entre 40,5 y 45,9 ns. Con decimales, la diferencia creció hasta 106,3 ns frente a un intervalo de 46,5 a 54,5 ns.

La causa no era el lenguaje en su conjunto. La versión en C enviaba los decimales a la función generalista strtod(), mientras que Rust utilizaba una ruta más rápida de su parser. Añadir unas 30 líneas para los valores habituales redujo el C optimizado a 19,2 ns con datos mixtos, 15,8 ns con enteros y 17,5 ns con decimales.

Una función pequeña expuso el coste de las abstracciones y del trabajo manual

La extensión en C ocupaba 14 408 B. Las variantes en Rust llegaban a 298 464 B y 320 864 B porque incluían la biblioteca estándar y código auxiliar. El C optimizado también exigía 104 líneas de código, gestión manual de buffers, comprobaciones de desbordamiento y cuidado con los cálculos de coma flotante.

Las versiones en Rust eran más breves y seguras de escribir. La decisión práctica para un desarrollador es clara: en una función pequeña y muy utilizada, C puede ofrecer menos latencia y un binario menor, pero lo cobra con código más complejo y más margen para errores. La elección depende de si pesa más cada nanosegundo o el tiempo de quienes mantendrán la extensión.

Es un microbenchmark acotado, no un duelo universal entre lenguajes

Las mediciones se realizaron en una sola máquina Linux x86-64 con 2 vCPU y 2 millones de filas. Cada cifra es el mejor resultado de 15 ejecuciones después de restar un recorrido base de la tabla. El método aísla bien el coste de la función, pero no muestra qué ocurre con otra biblioteca de C, otra arquitectura o una aplicación que haga más trabajo alrededor de la base de datos.

El informe también se produjo con ayuda de un LLM. Su credibilidad procede del código publicado, 40 pruebas escritas a mano y 8 millones de comparaciones fuzz sin discrepancias detectadas, no del origen de la prosa. Repetirlo en otra máquina sigue valiendo más que una conclusión rotunda basada en una sola tabla.

La reproducción dirá si la ventaja sobrevive fuera de la máquina del autor

La siguiente señal útil sería repetir el benchmark en macOS, ARM y con distintas implementaciones de la biblioteca estándar. También conviene eliminar la asignación de Vec de ambas variantes en Rust para separar el coste del lenguaje del que introduce la envoltura de la biblioteca.

Los datos reales plantearán otra prueba: frecuencia de valores incorrectos, longitud de las cadenas y uso de la ruta lenta mediante strtod(). Ahí veremos si los 19,2 ns sobreviven al contacto con datos que no fueron creados para un benchmark.

El veredicto de Lilith

Rust cruzó la meta por delante de la primera versión en C, pero el C optimizado se escapó en 2 millones de filas. En una función tan pequeña manda el parser que se mide, no la bandera del lenguaje.