Ingeniería / Benchmarks / CPython
Python 3.13, 3.14 y 3.15. ¿De verdad gana el más nuevo?
Quise responder con mediciones y no con promesas: 14 escenarios, tres versiones de CPython, tres sesiones separadas y un solo Intel Core i5-12600K. El resultado es bastante menos predecible que un número de versión.
La conclusión en una frase: Python 3.15 trae saltos enormes en codificaciones específicas y mejoras reproducibles en JSON, pero no es más rápido en todas las tareas. Algunas de las operaciones medidas siguen favoreciendo a Python 3.13.
Al comparar intérpretes, es fácil encontrar un resultado espectacular, convertirlo en titular y olvidar todo lo demás. El problema es que «Python es X% más rápido» no significa mucho sin saber qué ejecutamos, cómo lo medimos y cuánta incertidumbre aceptamos.
Por eso corrí el mismo protocolo con CPython 3.13.12, 3.14.3 y 3.15.0, instalado mediante pyenv, en un equipo Linux con Intel Core i5-12600K. Cada sesión tuvo 12 rondas por carga y versión, con 12 muestras internas por ejecución. En total: 504 ejecuciones por sesión y 18.144 muestras internas.
Primera sorpresa: JSON sí mejoró, y de forma consistente
En las tres sesiones, Python 3.15 superó a 3.14 tanto al serializar como al deserializar JSON. La mejora consolidada fue del 22,1% en velocidad de escritura y del 15,6% en velocidad de lectura para estas cargas concretas. No es una mejora universal de cualquier API: mide nuestro escenario de biblioteca estándar y su conjunto de datos.
¿Dónde fue más rápido Python 3.15 que 3.14?
Diferencia porcentual de velocidad · medias de log-razones pareadas agregadas por sesión · sin Base32/Base64 ni arranque sin site.
La longitud de las barras representa la magnitud del cambio, no una clasificación de rendimiento global. Dorado: 3.15 ganó; gris: 3.14 ganó. El arranque normal incluye el entorno de instalación y debe interpretarse por separado. Fuente: tres sesiones v3, archivo consolidado enlazado al final.
¿Base32 más de 200 veces más rápido? Sí, pero hay una explicación.
La diferencia más llamativa fue Base32: aproximadamente 219,6 veces la velocidad de 3.14 en nuestro escenario. Base64 alcanzó aproximadamente 2,11 veces la velocidad. Ambos efectos se repitieron en las tres sesiones.
Esto no significa que Python 3.15 haya multiplicado su rendimiento general por 200. El trabajo de optimización está localizado: Python 3.15 añade implementaciones nativas en binascii para ciertas codificaciones que anteriormente seguían otras rutas. Los detalles están en la documentación oficial de Python 3.15.
Una advertencia sobre promedios: mezclar un cambio de más de 200× con variaciones del 1% o 2% para calcular «la mejora media de Python» produciría una cifra llamativa y, probablemente, inútil para tomar decisiones.
Segunda sorpresa: Python 3.13 sigue ganando algunas pruebas
El escenario de funciones matemáticas con flotantes favoreció a 3.13 en las tres sesiones: 3.15 mostró una tasa de ejecución aproximadamente 17,6% inferior a 3.13 en esa prueba particular. Expresiones regulares y comprehensions también favorecieron a la versión más antigua frente a 3.15.
Eso no autoriza a afirmar que «Python 3.13 es mejor para matemáticas». El escenario combina determinadas operaciones, tamaños y datos; no representa NumPy, Pandas ni algoritmos científicos completos. Precisamente por ser inesperado, ese hallazgo merece validación adicional con pyperformance o un benchmark independiente.
Hubo buenas noticias para 3.14: asyncio resultó alrededor de 23,8% más rápido que 3.13 en nuestra tarea sintética, mientras que ETL puro mejoró aproximadamente un 9,5%. Tampoco debemos convertir estos resultados en predicciones directas sobre un backend real.
Los 14 escenarios, sin esconder los resultados pequeños
La siguiente tabla muestra los tiempos centrales de cada versión y la comparación consolidada 3.15 frente a 3.14. En milisegundos, menos es mejor. En la última columna, un valor positivo significa mayor velocidad de 3.15.
| Carga | 3.13.12 · ms | 3.14.3 · ms | 3.15.0 · ms | 3.15 vs. 3.14 |
|---|---|---|---|---|
| Enteros | 2,267 | 2,171 | 2,034 | +6,6% |
| Matemáticas / flotantes | 1,371 | 1,699 | 1,664 | +1,1% |
| Comprehensions | 1,115 | 1,315 | 1,294 | +2,6% |
| ETL sin librerías | 0,970 | 0,878 | 0,898 | -1,5% |
| JSON · escritura | 0,377 | 0,382 | 0,312 | +22,1% |
| JSON · lectura | 0,419 | 0,425 | 0,371 | +15,6% |
| Expresiones regulares | 0,659 | 0,845 | 0,789 | +7,3% |
| SQLite en memoria | 1,285 | 1,299 | 1,289 | +0,9% |
| asyncio | 2,035 | 1,616 | 1,646 | -1,4% |
| Hilos con espera de I/O | 6,506 | 6,555 | 6,180 | +1,4% |
| Base32 | 0,639 | 0,740 | 0,003 | ≈219,6× |
| Base64 | 0,005 | 0,006 | 0,003 | +110,5% |
| Arranque normal | 20,866 | 10,729 | 11,867 | -9,7% |
| Arranque sin site | 7,711 | 7,642 | 8,170 | -2,7% |
No se debe promediar esta tabla sin ponderar cargas representativas de una aplicación. Los tiempos centrales y los cambios de velocidad proceden de estimadores distintos (medianas descriptivas frente a log-razones pareadas), por lo que recalcular directamente el porcentaje desde los tiempos redondeados puede producir pequeñas diferencias.
El arranque: una mejora que no es lo que parece
3.14 arrancó aproximadamente al doble de velocidad que 3.13 en la prueba normal (proceso aislado con -I). Pero cuando deshabilitamos site con -S, la diferencia prácticamente desapareció. Eso sugiere que la instalación, la carga de módulos y el entorno influyen en esa aparente victoria.
La prueba de arranque merece su propio tratamiento porque no está midiendo lo mismo que un bucle de enteros o una operación de JSON. No utilizaría ese 2× para prometer que cualquier CLI duplicará su velocidad.
Cómo se diseñó el experimento
La idea fue alterar una sola variable principal: el intérprete. Las cargas se ejecutaron con entradas y semillas deterministas, comprobación SHA-256 de las salidas completas fuera del tramo cronometrado, calentamiento y orden contrabalanceado de versiones.
- Equipo: Intel Core i5-12600K, Linux 7.0.0-38-generic, glibc 2.39.
- Instalaciones: CPython 3.13.12, 3.14.3 y 3.15.0 mediante
pyenv; sin JIT y con GIL convencional. - Muestras: tres sesiones en dos días (tarde y noche del 14 de octubre, mañana del 15); 12 rondas, 12 muestras internas, presupuesto de 110 ms.
- Afinidad: CPU lógico 0 (P-core), sin aislamiento exclusivo del núcleo ni de su hilo hermano.
- Condiciones energéticas:
intel_pstate, governorpowersave, Turbo habilitado; la frecuencia pudo variar. - Inferencia: bootstrap jerárquico exploratorio, prueba bilateral exacta por sesión y corrección de Holm sobre 42 comparaciones.
El matiz estadístico que importa
Los cambios grandes mantuvieron dirección y magnitud comparables en las tres sesiones. Eso constituye evidencia de reproducibilidad local, pero no prueba una ventaja universal. Como solo hay tres sesiones verdaderamente separadas, la prueba exacta bilateral a nivel de sesión tiene un valor p mínimo de 0,25; después de la corrección de Holm los 42 valores ajustados son 1,0. No podemos afirmar significación inferencial ajustada al 5%.
Los intervalos bootstrap del 95% publicados en los datos son exploratorios: con tres sesiones, no deben interpretarse como garantías del comportamiento en equipos ajenos. Además, existió actividad variable en el hilo hermano, el procesador no estuvo aislado y las compilaciones no son estrictamente idénticas.
Qué conclusión NO extraigo: «Python 3.15 es 22% más rápido en general», «FastAPI irá 22% más rápido» o «ya no conviene usar 3.13». El experimento no mide esas afirmaciones.
Datos, código y reproducibilidad
Para que cualquiera pueda auditar los números, publico el script de la batería v3 y el consolidado utilizado para este artículo. El JSON conserva los resultados y comparaciones calculadas para las tres sesiones; el CSV facilita examinarlas en una planilla o en Pandas. Las tres capturas crudas de sesión son aproximadamente 1 MB cada una y se conservaron como archivos separados.
Reproduce con los mismos parámetros: --rounds 12 --samples 12 --budget-ms 110. El repositorio del sitio aloja el código publicado. Los archivos crudos originales se conservan y pueden incorporarse como un paquete adicional; el JSON consolidado no los sustituye íntegramente.
¿Qué Python elegiría después de esto?
Si mi carga de trabajo dedica mucho tiempo a serializar JSON o codificar Base32/Base64, 3.15 merece una prueba en mi propio entorno. Si mis tiempos dependen de cálculos, expresiones regulares o extensiones nativas, mediría primero el código real, sobre todo porque la compatibilidad de dependencias y los mecanismos internos cambian entre versiones.
La conclusión no es que la versión más nueva sea mala. Es que la pregunta útil nunca fue «¿cuál Python gana?», sino «¿qué cambia para mi aplicación, con qué magnitud y con qué evidencia?».
← Volver al blog técnico