paint-brush
“Python es lento” y otros mitos de una era moribundapor@oleksandrkaleniuk
13,386 lecturas
13,386 lecturas

“Python es lento” y otros mitos de una era moribunda

por Oleksandr Kaleniuk7m2023/01/11
Read on Terminal Reader

Demasiado Largo; Para Leer

La programación no es tan simple como solía ser. Python se interpreta y es lento, y C++ se compila y es rápido. Un lenguaje no es un compilador o un intérprete, es lo que es: un lenguaje: un conjunto de reglas sobre cómo debemos decirle a una computadora lo que debemos hacer.
featured image - “Python es lento” y otros mitos de una era moribunda
Oleksandr Kaleniuk HackerNoon profile picture


Cuando era pequeño, la programación era simple. Mi amigo tenía una computadora y había Basic y Assembly. Podrías escribir tu programa en Basic, que era más fácil de hacer pero tu programa sería lento, o podrías escribir algo en Assembly, que sería más difícil, pero tu programa se ejecutaría significativamente más rápido.


La explicación de esto también era simple. Basic era un intérprete, para ejecutar su programa, tenía que pasar por su código cada vez que lo invocaba e interpretarlo línea por línea. Si dice "IMPRIMIR X", el intérprete tuvo que encontrar una variable llamada "X", encontrar una rutina que imprima y llamar a la rutina encontrada para la variable encontrada.

Asamblea fue, bueno, asamblea. También interpretó su programa de alguna manera, pero solo una vez cuando ejecuta el ensamblador. Después de eso, su programa sería ejecutable sin interpretación. Los programas que necesitan interpretación se ejecutan más lentamente que los programas que no necesitan interpretación. Si, claro, son programas equivalentes.

Y en Básico y Montaje, por lo general lo eran. Basic es un lenguaje imperativo, ni siquiera demasiado amigable para la programación estructural. Incluso algo tan básico como una "función" no es una construcción de lenguaje incorporada sino un patrón: "GOSUB... RETURN", muy parecido a "call... ret" en Assembly.

Ahora avance rápido 30 años. Los idiomas sobran. Las computadoras están en todas partes. La programación ya no es simple. Mi departamento se gana el pan reescribiendo el código del investigador originalmente escrito en Python en C++ para el rendimiento porque el conocimiento común es que Python se interpreta y es lento, y C++ se compila y es rápido. Pero de alguna manera, cada año, esta reescritura se vuelve más y más difícil de lograr. Algo cambia y cambia rápido. Sin embargo, el conocimiento común no cambia, así que seguimos reescribiendo.

Pero ahora nos vemos obligados a optimizar todo como locos solo para justificar lo que hacemos. Aparece un algoritmo en Python, lo reescribimos en C++ de manera equivalente y, de repente, se vuelve 3 veces más lento. Eso... no es para lo que estamos aquí. Así que rediseñamos el algoritmo para impulsar y obtener el aumento de rendimiento que prometimos. Y la mayoría de las veces, dado que a los investigadores no les importa en absoluto el rendimiento y, en términos algorítmicos, dejan algunas frutas al alcance de la mano, esto funciona.

Aún así, todo este asunto ahora parece una estafa. Estamos haciendo que el código sea más lento al reescribirlo en C++ solo para poder hacerlo más rápido mediante la reingeniería del código. Entonces, ¿por qué no lo rediseñamos directamente en Python? ¡Ay! La cosa es que no conocemos Python. Conocemos un poco de Python, lo suficiente para leer y comprender, pero no lo suficiente para hacer programas ultrarrápidos en él.

Entonces, ¿qué hay que saber?

bibliotecas

La mayoría de las bibliotecas de Python están escritas en C o Fortran. NumPy core está escrito en C; Pandas - en Cython y C; SciPy : en Fortran, C y parcialmente C++. No tienen motivos para ser más lentos que lo que se escribió en C++, Rust o Julia. Aunque pueden ser más rápidos.

En nuestra empresa, atendemos tanto servicios en la nube como aplicaciones de escritorio. Y los usuarios de computadoras de escritorio se enojan cuando la nueva versión de su aplicación favorita deja de funcionar en su hardware sin razón aparente. Por lo tanto, mantenemos nuestro objetivo de compilaciones de escritorio antiguo. Muy viejo, como antes de Nehalem. De esta forma, nadie se enfada pero tampoco nadie disfruta de SSE3.


Por supuesto, una biblioteca computacional construida para un objetivo adecuado será generalmente más rápida que una biblioteca equivalente construida para una computadora genérica de 15 años con capacidades superescalares limitadas.

La buena noticia es que si está construyendo para una nube, puede configurar su plataforma de construcción de destino para que sea exactamente la máquina que adquiere, y luego sus bibliotecas de C++ se ejecutarán a la misma velocidad que las de Python y tal vez incluso un poco más rápido.

compiladores

Para ser honesto, todo el argumento sobre qué idioma es más rápido es ridículo. Un lenguaje no es un compilador o un intérprete, es lo que es: un lenguaje: un conjunto de reglas que especifican cómo debemos decirle a una computadora lo que queremos que haga. Un lenguaje es solo un conjunto de reglas, una especificación. Y nada más.


La distinción misma entre interpretación y compilación es algo del siglo pasado. Hoy en día, hay intérpretes de C como IGCC , PicoC o CCons , y hay compiladores de Python. Compiladores JIT como [PyPy] y compiladores clásicos de compilación antes de ejecutar como Codon (que también tiene capacidad JIT si solo desea que se compile parte de su código).


Codon se basa en LLVM, la misma infraestructura en la que se basan Rust, Julia o Clang. El código creado con Codon se ejecuta, más o menos, a los mismos niveles de rendimiento que el creado con cualquiera de ellos. Puede haber desventajas de rendimiento debido a la recolección de basura de Python o a los grandes tipos de datos nativos, pero ya no estamos hablando de 100x o 10x. LLVM hace su magia. convierte el código de Python en código de máquina para usted.


También existen mitos sobre la compilación justo a tiempo o JIT. Algunos dicen que es superior a la técnica habitual de compilar antes de ejecutar porque siempre compila para la arquitectura que tienen los usuarios y, por lo tanto, la explota de manera óptima. Algunos dicen que todavía hay una sobrecarga de compilación que con la compilación justo a tiempo también recae sobre los usuarios. Esto hace que los programas se ejecuten lentamente, ya que tienen que ejecutarse y compilarse al mismo tiempo.


El problema con ambos mitos es que ambos son verdaderos y ambos inútiles. Sí, JIT generalmente compila en un mejor código de máquina a menos que construyas tus archivos binarios explícitamente para la máquina de destino, lo que, por cierto, sucede con bastante frecuencia cuando implementas en una nube. Y sí, hay una penalización de compilación en tiempo de ejecución, pero es insignificante si tu tiempo de ejecución se mide en meses, lo que, de nuevo, cuando implementas en una nube, no es algo inaudito.


Así que hay pros y contras. Lo que es importante, Python (Codon específicamente) admite los modos compilar antes de ejecutar y JIT para que pueda elegir el que mejor se adapte a sus necesidades. Los compiladores tradicionales, como Clang, no tienen la opción JIT.

Numba y su modelo de kernel

Hablando de JIT, Numba es probablemente la tecnología más innovadora en el mundo de la programación Python ultrarrápida. Es un compilador, pero solo apunta a núcleos seleccionados, no a todo el programa. Usted, por supuesto, puede seleccionar qué se debe compilar y para qué plataforma. En esta configuración, puede ejecutar partes de su código en la CPU y otras, en GPGPU .


Técnicamente, se pueden crear backends para otros dispositivos especializados como la TPU de Google o incluso el acelerador fotónico de Lightmatters. Todavía no existe tal backend, esos muchachos decidieron implementar su propia biblioteca en su lugar. Pero, lo que es sintomático, también eligen proporcionar la interfaz para la computadora fotónica en Python para que puedas interactuar con Pytorch, Tensorflow u ONNX sin problemas.


Entonces Lightmatter aún no está allí. Pero NVidia lo es. Proporcionaron su backend CUDA para Numba y ahora puede escribir kernels en Python y ejecutarlos en hardware NVidia con la máxima eficiencia. C++ no tiene eso. Hay, sin embargo, un dialecto CU, procedente de NVidia , por supuesto, que se extiende a C++ para este mismo asunto. En Python, no tienes que extender el lenguaje en sí. Dado que Numba funciona como un compilador de kernel JIT, agregar un backend es solo cuestión de parchear una biblioteca.


Por lo tanto, el modelo de kernel apunta a la computación heterogénea. Puede ejecutar fragmentos de código en diferentes dispositivos, lo cual es bueno en sí mismo. Pero hay otra dimensión de la heterogeneidad en la que quizás no hayas pensado. Con el modelo de kernel, puede apuntar a diferentes kernels para diferentes contextos de computación y no necesariamente para dispositivos de hardware. Esto significa que si desea que un kernel sea rápido pero no particularmente preciso, puede compilarlo con una opción de "matemáticas rápidas". Pero si, en algún otro contexto, desea que el kernel sea preciso en lugar de rápido, puede reconstruir el mismo código sin la compensación.


Esto es algo difícil de lograr con los compiladores tradicionales en los que no se pueden cambiar las opciones de compilación en medio de una unidad de traducción. Bueno, con el modelo de kernel, cada kernel es su propia unidad de traducción.

Conclusión

Python no es lento. Tampoco es rápido. Es solo un idioma, un conjunto de reglas y palabras clave. Pero hay mucha gente que se acostumbró a estas reglas y estas palabras clave. Se sienten cómodos escribiendo en Python y están interesados en mejorar Python para ellos.


Esta base de usuarios es lo suficientemente grande como para atraer tanto a nuevas empresas emergentes con tecnologías revolucionarias como Lightmatter y sus computadoras fotónicas como a empresas bien establecidas con décadas de experiencia en computación de alto rendimiento como NVidia. Todas estas personas están muy involucradas en hacer de Python un mejor... no lenguaje, por supuesto, sino un entorno. El entorno en el que escribir programas ultrarrápidos es un poco más difícil que escribir programas lentos y desechables.


En total, están haciendo un gran progreso. Python es cada vez más rápido cada año. En este punto, olvídese de las computadoras fotónicas si puede, los programas escritos en Python a menudo se ejecutan a la par que los escritos en Julia, C ++ o Rust. Pero Python no se detendría ahí. Se está volviendo más rápido de lo que los compiladores tradicionales se están volviendo más fáciles de usar.


Esté preparado para ver que en unos años los compiladores de Python: PyPy, Numba, o algo completamente nuevo, tomarán prestadas técnicas de Spiral o Herbie para generar código de manera mucho más efectiva que ningún compilador tradicional podría acercarse. Después de todo, escribir un backend JIT nuevo en Python es mucho más fácil que volver a imaginar toda la infraestructura LLVM.