Cuántas líneas de C se necesitan para ejecutar `a + b` en Python
La expresión aparentemente simple `a + b` en Python oculta una gran cantidad de código C y de maquinaria en tiempo de ejecución, desde el despatch dinámico y los métodos mágicos hasta los enteros de precisión arbitraria y la búsqueda de atributos basada en hash. Los comentaristas usan esto como punto de partida para examinar por qué CPython es relativamente lento frente a runtimes con JIT (como V8, LuaJIT o PyPy), señalando decisiones de diseño como la dinamicidad ubicua de objetos, la API de C y el hashing orientado a la seguridad. Muchos sostienen que Python destaca como “pegamento” alrededor de bibliotecas rápidas en C o C++, más que como motor de cómputo de alto rendimiento, y que lograr mejoras sustanciales requeriría cambios arquitectónicos profundos y no simples retoques.
Alcance de la pregunta original
- Muchos señalan que el artículo en realidad no responde a “cuántas líneas de C” se ejecutan para
a + b. - Varios argumentan que la noción está mal planteada: una vez que permites sobrecarga de operadores (
__add__/__radd__),a + bpuede realizar un trabajo arbitrariamente complejo. - Otros sugieren que al menos podrías medir una ejecución concreta usando cobertura de código o profiling, pero incluso eso depende de la ruta de ejecución y de la entrada.
Despatch dinámico, métodos mágicos y complejidad
- La discusión enfatiza cuánta maquinaria hay detrás incluso de operaciones simples en CPython: el modelo de objetos, el despatch dinámico mediante slots de tipos, los métodos mágicos
__dunder__, los descriptores y las excepciones para la iteración. - En comparación con lenguajes como JS o Lua, Python expone más “magia” de forma ubicua en objetos ordinarios, lo que dificulta la optimización agresiva y el JIT.
- La subclase de builtins (por ejemplo,
int) y los descriptores personalizados complican aún más la especialización.
Rendimiento, JITs y compromisos de diseño
- Varios comentaristas sostienen que CPython no está pensado intencionalmente para el rendimiento; optimiza por simplicidad y por interoperabilidad con C, y Python se entiende mejor como “pegamento” sobre bibliotecas nativas rápidas.
- Otros responden que otros lenguajes dinámicos (JS, LuaJIT, Smalltalk, JITs de Ruby) muestran que un alto rendimiento es posible con suficiente ingeniería.
- Hay debate sobre si el diseño del lenguaje Python o la API de C y el ecosistema son los principales obstáculos para un JIT de primer nivel.
- Se mencionan los trabajos recientes para acelerar CPython y JITs externos (PyPy, Pyston); las mejoras logradas hasta ahora se ven como reales pero modestas en comparación con la revolución del JIT en JS.
Hashing, seguridad y velocidad
- Un hilo paralelo retoma afirmaciones de que cambiar la función de hash de Python podría duplicar el rendimiento.
- Varios comentaristas lo discuten: los hashes de cadenas se cachean, los nombres de símbolos se internan, y los benchmarks que mostraban ganancias enormes fueron criticados por poco realistas.
- El SipHash aleatorizado de Python (frente al antiguo FNV) se usa principalmente para resistencia a HashDoS; el impacto en rendimiento en cargas reales parece pequeño.
- Se discute el hashing por identidad para enteros; interactúa con la implementación de tablas hash y puede causar patologías con ciertas distribuciones de enteros.
Ergonomía de C frente a Python y “usa C directamente”
- Algunos sugieren que podría ser “más fácil” usar C directamente, pero otros destacan el boiloerplate drásticamente menor de Python y su soporte incorporado para enteros de precisión arbitraria.
- Los ejemplos muestran cuántas pocas líneas de Python implementan aritmética de precisión arbitraria, en comparación con las muchas líneas de C o C++ necesarias para igualar el comportamiento y la seguridad.