paint-brush
Sí, Python es lento y no me importapor@nhumrich
87,150 lecturas
87,150 lecturas

Sí, Python es lento y no me importa

por Nick Humrich2016/11/18
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

Estoy tomando un descanso de mi discusión sobre asyncio en Python para hablar sobre algo que ha estado en mi mente recientemente: la velocidad de Python. Para aquellos que no saben, soy un poco fanático de Python, y uso agresivamente Python en todas partes que puedo. Una de las mayores quejas que tiene la gente contra Python es que es lento. Algunas personas casi se niegan incluso a probar python porque es más lento que X. Estos son mis pensamientos sobre por qué debería probar python, a pesar de que es lento.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Sí, Python es lento y no me importa
Nick Humrich HackerNoon profile picture

Una diatriba sobre sacrificar el rendimiento por la productividad.

Estoy tomando un descanso de mi discusión sobre asyncio en Python para hablar sobre algo que ha estado en mi mente recientemente: la velocidad de Python. Para aquellos que no saben, soy un poco fanático de Python, y uso agresivamente Python en todas partes que puedo. Una de las mayores quejas que tiene la gente contra Python es que es lento. Algunas personas casi se niegan incluso a probar python porque es más lento que X. Estos son mis pensamientos sobre por qué debería probar python, a pesar de que es lento.

La velocidad ya no importa

Antes, los programas tardaban mucho en ejecutarse. Las CPU eran caras, la memoria era cara. El tiempo de ejecución de un programa solía ser una métrica importante. Las computadoras eran muy caras, al igual que la electricidad para hacerlas funcionar. La optimización de estos recursos se hizo por una eterna ley empresarial:

Optimice su recurso más caro.

Históricamente, el recurso más costoso era el tiempo de ejecución de la computadora. Esto es lo que condujo al estudio de la informática que se centra en la eficiencia de diferentes algoritmos. Sin embargo, esto ya no es cierto, ya que el silicio ahora es barato. Como realmente barato. El tiempo de ejecución ya no es su recurso más costoso. El recurso más caro de una empresa es ahora el tiempo de sus empleados. O en otras palabras, usted. Es más importante hacer las cosas que hacerlo rápido. De hecho, esto es tan importante que lo pondré nuevamente aquí como si fuera una cita (para aquellos que solo están navegando):

Es más importante hacer las cosas que hacerlo rápido.

Podría estar diciendo: "Mi empresa se preocupa por la velocidad, construyo una aplicación web y todas las respuestas deben ser más rápidas que x milisegundos". O, "Hemos tenido clientes cancelando porque piensan que nuestra aplicación es demasiado lenta". No estoy tratando de decir que la velocidad no importa en absoluto, simplemente estoy tratando de decir que ya no es lo más importante; no es su recurso más caro.

¡Velocidad!

La velocidad es lo único que importa

Cuando dices velocidad en el contexto de la programación, normalmente te refieres a rendimiento, también conocido como ciclos de CPU. Cuando su CEO dice velocidad en el contexto de la programación, se refiere a la velocidad del negocio. La métrica más importante es el tiempo de comercialización. En última instancia, no importa qué tan rápido sea su producto/aplicación web. No importa en qué idioma esté escrito. Ni siquiera importa cuánto dinero se necesita para ejecutarlo. Al final del día, lo único que hará que su empresa sobreviva o muera es el tiempo de comercialización. No me refiero solo a la idea inicial de cuánto tiempo se tarda en ganar dinero, sino más bien al marco de tiempo de "desde la idea hasta las manos de los clientes". La única forma de sobrevivir en los negocios es innovar más rápido que sus competidores. No importa cuántas buenas ideas se te ocurran si tus competidores "envían" antes que tú. Tienes que ser el primero en comercializar, o al menos mantenerte al día. Una vez que disminuyas la velocidad, habrás terminado.

La única forma de sobrevivir en los negocios es innovar más rápido que sus competidores.

Un caso de microservicios

Empresas como Amazon, Google y Netflix entienden la importancia de moverse rápido. Han creado un sistema empresarial en el que pueden moverse rápido e innovar rápidamente. Los microservicios son la solución a su problema. Este artículo no tiene nada que ver con si debería o no usar microservicios, pero al menos acepte que Amazon y Google piensan que deberían usarlos.

Los microservicios son inherentemente lentos. El concepto mismo de un microservicio es romper un límite mediante una llamada de red. Esto significa que está tomando lo que era una llamada de función (un par de ciclos de CPU) y convirtiéndola en una llamada de red. No hay mucho que puedas hacer que sea peor en términos de rendimiento. Las llamadas de red son realmente lentas en comparación con la CPU. Pero estas grandes empresas aún eligen usar microservicios. Realmente no hay una arquitectura más lenta que los microservicios que yo sepa. La mayor desventaja de los microservicios es el rendimiento, pero la mayor ventaja es el tiempo de comercialización. Al crear equipos en torno a proyectos más pequeños y bases de código, una empresa puede iterar e innovar a un ritmo mucho más rápido. Esto solo demuestra que las empresas muy grandes también se preocupan por el tiempo de comercialización, no solo por las nuevas empresas.

La CPU no es su cuello de botella

Si escribe una aplicación de red, como un servidor web, lo más probable es que el tiempo de CPU no sea el cuello de botella de su aplicación. Cuando su servidor web maneja una solicitud, probablemente hace un par de llamadas de red, como a su base de datos, o quizás a un servidor de caché como Redis. Si bien estos servicios en sí mismos pueden ser rápidos, la llamada de la red a ellos es lenta. Hay un artículo de blog realmente excelente sobre las diferencias de velocidad de ciertas operaciones. En el artículo, el autor escala los tiempos de ciclo de la CPU en tiempos humanos más comprensibles. Si un solo ciclo de CPU fuera el equivalente a 1 segundo, entonces una llamada de red de California a Nueva York sería el equivalente a 4 años. Así de lenta es la red. Para algunas estimaciones aproximadas, digamos que una llamada de red normal dentro del mismo centro de datos tarda unos 3 ms. Eso sería el equivalente a 3 meses en nuestra “escala humana”. Ahora imagine que su programa consume mucha CPU, se necesitan 100,000 ciclos para responder a una sola llamada. Eso sería el equivalente a poco más de 1 día. Ahora digamos que usa un idioma que es 5 veces más lento, ahora toma alrededor de 5 días. Bueno, compare eso con nuestra llamada de red de 3 meses, y la diferencia de 4 días realmente no importa mucho. Si alguien tiene que esperar al menos 3 meses por un paquete, no creo que 4 días adicionales realmente le importen tanto.

Lo que esto significa en última instancia es que, incluso si Python es lento, no importa. La velocidad del idioma (o el tiempo de CPU) casi nunca es el problema. Google en realidad hizo un estudio sobre este mismo concepto, y escribieron un artículo al respecto . El documento habla sobre el diseño de un sistema de alto rendimiento. En la conclusión dicen:

Puede parecer paradójico utilizar un lenguaje interpretado en un entorno de alto rendimiento, pero hemos descubierto que el tiempo de CPU rara vez es el factor limitante; la capacidad de expresión del lenguaje significa que la mayoría de los programas son pequeños y pasan la mayor parte de su tiempo en E/S y código de tiempo de ejecución nativo. Además, la flexibilidad de una implementación interpretada ha sido útil, tanto para facilitar la experimentación a nivel lingüístico como para permitirnos explorar formas de distribuir el cálculo entre muchas máquinas.

o, para enfatizar:

el tiempo de CPU rara vez es el factor limitante

¿Qué pasa si el tiempo de CPU es un problema?

Es posible que esté diciendo: "Eso es genial y todo eso, pero hemos tenido problemas en los que la CPU era nuestro cuello de botella y causaba una gran ralentización de nuestra aplicación web", o "El idioma x requiere mucho menos hardware para ejecutarse que el idioma y en el servidor". Todo esto podría ser cierto. Lo maravilloso de los servidores web es que puede equilibrarlos casi infinitamente. En otras palabras, tírale más hardware. Claro, Python podría requerir un mejor hardware que otros lenguajes, como C. Simplemente agregue hardware a su problema de CPU. El hardware es muy barato en comparación con su tiempo. Si ahorra un par de semanas de tiempo en productividad en un año, eso pagará con creces el costo adicional del hardware.

Entonces, ¿Python es más rápido?

Todo este tiempo he estado hablando de que lo más importante es el tiempo de desarrollo. Entonces, la pregunta sigue siendo: ¿Python es más rápido que el lenguaje X en lo que respecta al tiempo de desarrollo? Como anécdota, yo, google y varios otros , podemos decirle cuánto más productivo es Python. Abstrae muchas cosas para usted, lo que lo ayuda a concentrarse en lo que realmente está tratando de codificar, sin quedarse atrapado en las malas hierbas de las cosas pequeñas, como si debe usar un vector o una matriz. Pero es posible que no le guste creer en la palabra de otros, así que veamos algunos datos más empíricos.

En su mayor parte, este debate sobre si python es más productivo o no realmente se reduce a secuencias de comandos (o lenguajes dinámicos) frente a lenguajes tipificados estáticamente. Creo que se acepta comúnmente que los lenguajes tipificados estáticamente son menos productivos, pero aquí hay un buen artículo que explica por qué. En términos de Python específicamente, aquí hay un buen resumen de un estudio que analizó cuánto tiempo llevó escribir código para el procesamiento de cadenas en varios idiomas.

Cuánto tiempo lleva escribir una aplicación de procesamiento de cadenas en varios idiomas. (Prechelt y Garret)

Python es más del doble de productivo que Java en el estudio anterior. Hay algunos otros estudios que muestran lo mismo también. Rosetta Code hizo un estudio bastante profundo de la diferencia de los lenguajes de programación. En el documento, comparan Python con otros lenguajes de secuencias de comandos/interpretados y dicen:

Python tiende a ser el más conciso, incluso frente a los lenguajes funcionales (1,2 a 1,6 veces más corto en promedio)

La tendencia común parece ser que las "líneas de código" siempre son menos en Python. Las líneas de código pueden sonar como una métrica terrible, pero varios estudios , incluidos los dos ya mencionados, muestran que el tiempo empleado por línea de código es aproximadamente el mismo en todos los idiomas. Por lo tanto, limitar el número de líneas de código aumenta la productividad. Incluso el mismo codinghorror (un programador de C#) escribió un artículo sobre cómo Python es más productivo .

Creo que es justo decir que Python es más productivo que muchos otros lenguajes. Esto se debe principalmente al hecho de que Python viene con "baterías incluidas" y tiene muchas bibliotecas de terceros. Aquí hay un artículo simple que habla sobre las diferencias entre Python y X. Si no sabe por qué Python es tan "pequeño" y productivo, lo invito a aprovechar esta oportunidad para aprender algo de Python y ver por sí mismo. Aquí está su primer programa:

importar __hola__

Pero, ¿y si la velocidad realmente importa?

rendimiento en tiempo de ejecución

El tono de los puntos anteriores puede hacer que parezca que la optimización y la velocidad no importan en absoluto. Pero la verdad es que hay muchas ocasiones en las que el rendimiento del tiempo de ejecución realmente importa. Un ejemplo es que tiene una aplicación web y hay un extremo específico que está tardando mucho en responder. Usted sabe qué tan rápido debe ser y cuánto debe mejorarse.

En nuestro ejemplo, sucedieron un par de cosas:

  1. Notamos un solo punto final que funcionaba lentamente
  2. Lo reconocemos como lento porque tenemos una métrica de lo que se considera lo suficientemente rápido y está fallando en esa métrica.

No tenemos que micro-optimizar todo en una aplicación. Todo solo necesita ser "lo suficientemente rápido". Sus usuarios pueden notar si un punto final tarda un par de segundos en responder, pero no notarán que mejoró el tiempo de respuesta de una llamada de 35 ms a 25 ms. "Suficientemente bueno", realmente es todo lo que necesitas lograr. Descargo de responsabilidad: probablemente debería decir que hay algunas aplicaciones, como las aplicaciones de ofertas en tiempo real, que necesitan microoptimizaciones, y cada milisegundo es importante. Pero esa es la excepción, no la regla.

Para descubrir cómo optimizar el punto final, su primer paso sería perfilar el código e intentar averiguar dónde está el cuello de botella. Después de todo:

Cualquier mejora realizada en cualquier lugar además del cuello de botella es una ilusión. — Gen Kim

Si sus optimizaciones no tocan el cuello de botella, está perdiendo el tiempo y no está solucionando el problema real. No obtendrá mejoras serias hasta que optimice el cuello de botella. Si intenta optimizar antes de saber cuál es el cuello de botella, terminará jugando al topo con partes de su código. La optimización del código antes de medir y determinar dónde está el cuello de botella se conoce como "optimización prematura". A Donald Knuth se le suele atribuir la siguiente cita, pero afirma que se la robó a otra persona:

La optimización prematura es la fuente de todos los males.

Al hablar sobre el mantenimiento de las bases del código, la cita más completa de Donald Knuth es:

Deberíamos olvidarnos de las pequeñas eficiencias, digamos alrededor del 97% del tiempo: la optimización prematura es la raíz de todos los males. Sin embargo, no debemos dejar pasar nuestras oportunidades en ese crítico 3%.

En otras palabras, dice que la mayoría de las veces, debe olvidarse de optimizar su código. Casi siempre es lo suficientemente bueno. En los casos en que no es lo suficientemente bueno, generalmente solo necesitamos tocar el tres por ciento de la ruta del código. No gana ningún premio al hacer que su punto final sea un par de nanosegundos más rápido porque usó una declaración if en lugar de una función, por ejemplo. Optimice solo después de medir.

La optimización prematura incluye llamar a ciertos métodos más rápidos , o incluso usar una estructura de datos específica porque generalmente es más rápida. Computer Science argumenta que si un método o algoritmo tiene el mismo crecimiento asintótico (o Big-O) que otro, entonces son equivalentes, incluso si uno es 2 veces más lento en la práctica. Las computadoras son tan rápidas que el crecimiento computacional de un algoritmo a medida que aumentan los datos/el uso importa mucho más que la velocidad real en sí. En otras palabras, si tiene dos funciones O(log n) , pero una es el doble de lenta, en realidad no importa. A medida que aumenta el tamaño de los datos, ambos se ralentizan al mismo ritmo. Por eso la optimización prematura es la raíz de todos los males; Nos hace perder el tiempo y, de todos modos, casi nunca ayuda a nuestro rendimiento general.

En términos de Big-O, podría argumentar que todos los idiomas son O (n) para su programa, donde n son líneas de código o instrucciones. Todos crecen al mismo ritmo para las mismas instrucciones. No importa cuán lento sea un lenguaje/tiempo de ejecución, en términos de crecimiento asintótico, todos los lenguajes son iguales. Bajo esta lógica, podría decir que elegir un idioma para su aplicación simplemente porque es "rápido" es la forma definitiva de optimización prematura. Estás eligiendo algo supuestamente rápido sin medir, sin entender dónde va a estar el cuello de botella.

Elegir un idioma para su aplicación simplemente porque es "rápido" es la mejor forma de optimización prematura.

Optimización de Python

Una de mis cosas favoritas de Python es que te permite optimizar el código poco a poco. Digamos que tiene un método en Python que encuentra que es su cuello de botella. Lo ha optimizado varias veces, posiblemente siguiendo alguna guía aquí y allá , y ahora está en el punto en el que está bastante seguro de que Python es el cuello de botella. Python tiene la capacidad de llamar al código C, lo que significa que puede reescribir este método en C para reducir el problema de rendimiento. Puede hacer este método de uno en uno. Este proceso le permite escribir métodos de cuello de botella bien optimizados en cualquier lenguaje que compile en un ensamblador compatible con C. Esto le permite permanecer en Python la mayor parte del tiempo y solo acceder a las cosas de nivel inferior cuando realmente las necesita.

Hay un lenguaje llamado Cython que es un superconjunto de Python. Es casi una fusión de Python y C, y es un lenguaje de escritura progresiva. Cualquier código de Python es un código Cython válido, y Cython se compila en código C. Con Cython, puede escribir un módulo o método y progresar lentamente a más y más C-Types y rendimiento. Puede entremezclar los tipos de C y los tipos de pato de Python. Al usar Cython, obtiene la combinación perfecta de optimización solo en el cuello de botella y la belleza de Python en cualquier otro lugar.

Captura de pantalla de Eve online: un MMO espacial escrito en Python

Cuando finalmente se encuentra con un muro de problemas de rendimiento de Python, no necesita mover toda su base de código a un idioma diferente. Casi siempre puede obtener el rendimiento que necesita simplemente reescribiendo un par de métodos en Cython. Esta es la estrategia que adopta Eve Online . Eve es un juego de computadora multijugador masivo que usa Python y Cython para toda la pila. Logran un rendimiento a nivel de juego al optimizar los cuellos de botella en C/Cython. Si funciona para ellos, debería funcionar para la mayoría de las personas. Alternativamente, también hay otras formas de optimizar su python. Por ejemplo, PyPy es una implementación JIT de Python que podría brindarle mejoras significativas en el tiempo de ejecución para aplicaciones de ejecución prolongada (como un servidor web) simplemente intercambiando CPython (la implementación predeterminada) con PyPy.

Repasemos algunos de los puntos principales:

  • Optimice para su recurso más caro. Eso es USTED , no la computadora.
  • Elija un lenguaje/marco/arquitectura que le ayude a desarrollar rápidamente (como Python). No elija tecnologías simplemente porque son rápidas.
  • Cuando tenga problemas de rendimiento: encuentre su cuello de botella
  • Lo más probable es que su cuello de botella no sea la CPU o Python en sí.
  • Si Python es su cuello de botella (ya ha optimizado algoritmos/etc.), mueva el punto de acceso a Cython/C
  • Vuelva a disfrutar de hacer las cosas rápidamente

Espero que hayan disfrutado leyendo este artículo tanto como yo disfruté escribiéndolo. Si desea dar las gracias, simplemente presione el botón de aplausos o manténgalo presionado. Además, si desea hablar conmigo sobre Python en algún momento, puede ponerse en contacto conmigo en Twitter (@nhumrich) o puede encontrarme en el canal flojo de Python .