SQLite: parse_number() en C es 2x más rápido que Rust

Qué es parse_number() y por qué tus datos lo necesitan

Convertir una cadena como -$1,234.56 o 12.5% en un valor numérico que SQLite pueda sumar y ordenar cuesta 16 nanosegundos por fila en la mejor implementación en C, frente a 41 nanosegundos en la versión más rápida escrita en Rust. Esa es la conclusión central del experimento que Simon Willison publicó el 8 de noviembre en su blog, con el código y los tests alojados en su repositorio simonw/research.

El problema que resuelve es cotidiano para cualquier equipo que hace analítica: los datos llegan como texto. Un CSV exportado de un CRM trae "$1,234.56", un reporte de marketing trae "12.5%", una planilla financiera trae "-$980". SQLite no puede sumar eso directamente, y la alternativa habitual —encadenar replace() para limpiar comas, signos de peso y porcentajes— es lenta y frágil.

La función parse_number(value) hace exactamente eso: quita ,, $, £ y %, respeta un signo negativo inicial y devuelve NULL cuando no puede interpretar el texto. Simple en la definición, interesante en la implementación.

¿Y esto cómo se aplica en tu negocio?

Dueños de negocio de Latinoamérica y España usan un agente de IA para ganar más y recuperar su tiempo. El Kit «tu mano derecha con IA» te muestra cómo en un video de 11 minutos, con una ficha para armar el tuyo. CAR está cerrada por ahora; al anotarte, te avisamos cuando abra.

👥 Quiero el Kit →

C vs Rust: los números del experimento

Willison partió de un prompt a Claude (Opus 5.5) pidiendo comparar la misma extensión escrita en C y en Rust, midiendo rendimiento, complejidad del código y tamaño del binario resultante. El primer resultado fue contraintuitivo: la versión en Rust era más rápida que la de C, porque el equivalente en Rust de strtod —la función estándar de C para convertir texto a número— resultaba aproximadamente el doble de veloz.

El segundo paso fue pedirle a Claude una función propia de parseo en C que alcanzara o superara a Rust. Lo logró. Estos son los costos netos por fila sobre 2 millones de filas que reporta el informe (menor es mejor):

Escenario C original C optimizado rusqlite sqlite-loadable
Datos mixtos 67 ns 19 ns 46 ns 41 ns
Solo enteros 25 ns 16 ns 36 ns 32 ns
Solo decimales 106 ns 18 ns 55 ns 47 ns

El C optimizado quedó entre 2 y 2,5 veces más rápido que las dos implementaciones en Rust, y muy por delante de la solución basada en replace() de SQL. Además, las cuatro versiones coincidieron en 8 millones de verificaciones de tipo fuzz, lo que da una señal razonable de que la optimización no rompió la semántica.

Por qué el C «lento» se volvió el más rápido

La diferencia está en un detalle de implementación, no en el lenguaje. La primera versión en C delegaba el parseo de decimales en strtod(), una función genérica que arrastra validaciones y manejo de casos que aquí no hacen falta. Eso la hacía especialmente lenta con decimales: 106 ns por fila.

La versión optimizada agregó un camino rápido y compacto para los formatos que realmente aparecen en datos de negocio, y el costo cayó a 18 ns en ese mismo escenario. La lección es transferible: en procesamiento de datos, el cuello de botella casi nunca es el lenguaje, sino cuánto trabajo genérico estás haciendo por cada registro.

El tamaño del binario: 14 KB contra 321 KB

El otro eje de la comparación es el peso del artefacto compilado. Según el informe, la extensión en C ocupa 14 KB, mientras que las versiones en Rust llegan a 321 KB con Rusqlite y 298 KB con sqlite-loadable-rs.

Esa brecha es consistente con lo que documenta el propio proyecto sqlite-loadable-rs de Alex Garcia: su README señala que una extensión «hello world» en C pesa 17 KB y una equivalente en Rust 469 KB, mientras que una en Go ronda los 2,2 MB. El mismo README aclara que, en general, una extensión construida con sqlite-loadable-rs es entre 10% y 15% más lenta que una en C, aunque también advierte que eso depende del trabajo real de cada extensión: cita que sqlite-xsv es 1,5 a 1,7 veces más rápido que la extensión CSV oficial escrita en C, y que sqlite-regex duplica la velocidad de la extensión regexp.

Es decir: Rust no es intrínsecamente más lento. El framework y el tipo de operación pesan más que el lenguaje.

¿Qué significa esto para tu startup?

Si tu equipo procesa datos en SQLite —y con la cantidad de productos que hoy embeben SQLite en el cliente, es probable que sí— la decisión entre C y Rust no debería tomarla el gusto del equipo, sino el perfil del problema.

  • Mide antes de elegir lenguaje. El caso de parse_number() muestra que una implementación en C puede ser 2x más rápida o 2x más lenta que Rust según cómo esté escrita. Un benchmark de 2 millones de filas con tus datos reales te cuesta una tarde y evita meses de arrepentimiento.
  • Prioriza el camino rápido para tus formatos reales. Si el 95% de tus valores vienen en tres o cuatro formatos, escribir un parser específico para esos casos rinde mucho más que optimizar el caso genérico. Fue exactamente lo que cerró la brecha de decimales en C.
  • Considera el peso del binario si distribuyes en el cliente. 14 KB contra 321 KB no importa en un servidor, pero sí importa si tu extensión viaja dentro de una app de escritorio, un plugin o un entorno embebido con restricciones de tamaño.
  • No descartes Rust por el tamaño. El ecosistema ya resolvió buena parte del problema: desde rusqlite 0.30.0 existe soporte de extensiones cargables con la feature loadable_extension, y proyectos como sqlite-regex demuestran que Rust puede superar a C en tareas específicas.

Cómo aprovechar el informe en tu próximo sprint

El valor práctico del reporte de Willison no está en el resultado —C gana— sino en el método. Se formuló una pregunta concreta, se generaron cuatro implementaciones comparables, se midieron con el mismo dataset y se validaron con fuzz testing antes de sacar conclusiones.

Ese flujo es replicable con cualquier herramienta de IA generativa: pedir dos o tres variantes de la misma solución, exigir una tabla de rendimiento y un conjunto de tests que verifiquen equivalencia funcional. La parte que sigue siendo tuya es definir qué se mide y con qué datos.

Para un founder técnico, la conclusión más útil es que el costo de escribir una extensión propia de SQLite bajó lo suficiente como para dejar de depender de replace() en consultas críticas. Y que la respuesta a «¿C o Rust?» es, otra vez, «depende de qué estés midiendo».

Fuentes

¿te gustó o sirvió lo que leíste?, Por favor, comparte.

¿Y esto cómo se aplica en tu negocio?

Dueños de negocio de Latinoamérica y España usan un agente de IA para ganar más y recuperar su tiempo. El Kit «tu mano derecha con IA» te muestra cómo en un video de 11 minutos, con una ficha para armar el tuyo. CAR está cerrada por ahora; al anotarte, te avisamos cuando abra.

👥 Quiero el Kit →

Daily Shot: Tu ventaja táctica

Lo que pasó en las últimas 24 horas, resumido para que tú no tengas que filtrarlo.

Suscríbete para recibir cada mañana la curaduría definitiva del ecosistema startup e inversionista. Sin ruido ni rodeos, solo la información estratégica que necesitas para avanzar:

  • Venture Capital & Inversiones: Rondas, fondos y movimientos de capital.
  • IA & Tecnología: Tendencias, Web3 y herramientas de automatización.
  • Modelos de Negocio: Actualidad en SaaS, Fintech y Cripto.
  • Propósito: Erradicar el estancamiento informativo dándote claridad desde tu primer café.

📡 El Daily Shot Startupero

Noticias del ecosistema startup en 2 minutos. Gratis, todos los días.

Share to...