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 + b puede 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.