Soy nuevo en gevents y greenlets. ¡Encontré una buena documentación sobre cómo trabajar con ellos, pero ninguno me dio justificación sobre cómo y cuándo debería usar greenlets!
- ¿En qué son realmente buenos?
- ¿Es una buena idea usarlos en un servidor proxy o no?
- ¿Por qué no hilos?
De lo que no estoy seguro es de cómo nos pueden proporcionar concurrencia si son básicamente co-rutinas.
threading.Thread
realidad es un hilo del sistema operativo con todas las ramificaciones. Entonces, en realidad no es tan simple. Por cierto, Jython no tiene GIL AFAIK y PyPy también está tratando de deshacerse de él.Respuestas:
Los greenlets proporcionan concurrencia pero no paralelismo. La concurrencia es cuando el código puede ejecutarse independientemente de otro código. El paralelismo es la ejecución de código concurrente simultáneamente. El paralelismo es particularmente útil cuando hay mucho trabajo por hacer en el espacio de usuario, y eso es típicamente un trabajo pesado de CPU. La concurrencia es útil para resolver problemas, ya que permite programar y gestionar diferentes partes más fácilmente en paralelo.
Los greenlets realmente brillan en la programación de red donde las interacciones con un socket pueden ocurrir independientemente de las interacciones con otros sockets. Este es un ejemplo clásico de concurrencia. Debido a que cada greenlet se ejecuta en su propio contexto, puede continuar utilizando API síncronas sin subprocesos. Esto es bueno porque los subprocesos son muy caros en términos de memoria virtual y sobrecarga del núcleo, por lo que la concurrencia que puede lograr con los subprocesos es significativamente menor. Además, el enhebrado en Python es más costoso y más limitado de lo habitual debido al GIL. Las alternativas a la concurrencia generalmente son proyectos como Twisted, libevent, libuv, node.js, etc., donde todo su código comparte el mismo contexto de ejecución y registra controladores de eventos.
Es una excelente idea usar greenlets (con soporte de red apropiado, como a través de gevent) para escribir un proxy, ya que su manejo de solicitudes puede ejecutarse de forma independiente y debe escribirse como tal.
Los Greenlets proporcionan concurrencia por las razones que expuse anteriormente. La concurrencia no es paralelismo. Al ocultar el registro de eventos y realizar la programación para usted en llamadas que normalmente bloquearían el hilo actual, proyectos como gevent exponen esta concurrencia sin requerir cambios a una API asincrónica, y a un costo significativamente menor para su sistema.
fuente
import hashlib def checksum_md5(filename): md5 = hashlib.md5() with open(filename,'rb') as f: for chunk in iter(lambda: f.read(8192), b''): md5.update(chunk) return md5.digest()
Tomando la respuesta de @ Max y agregando cierta relevancia para el escalado, puede ver la diferencia. Lo logré cambiando las URL para que se llenen de la siguiente manera:
Tuve que abandonar la versión multiproceso ya que cayó antes de tener 500; pero a 10,000 iteraciones:
Entonces puede ver que hay una diferencia significativa en E / S usando gevent
fuente
Al corregir la respuesta de @TemporalBeing anterior, los greenlets no son "más rápidos" que los hilos y es una técnica de programación incorrecta generar 60000 hilos para resolver un problema de concurrencia, en cambio es apropiado un pequeño grupo de hilos. Aquí hay una comparación más razonable (de mi publicación de reddit en respuesta a las personas que citan esta publicación SO).
Aquí hay algunos resultados:
El malentendido que todo el mundo tiene acerca de no bloquear IO con Python es la creencia de que el intérprete de Python puede atender el trabajo de recuperar resultados de sockets a gran escala más rápido de lo que las conexiones de red pueden devolver IO. Si bien esto es cierto en algunos casos, no es cierto con tanta frecuencia como la gente piensa, porque el intérprete de Python es muy, muy lento. En mi blog aquí , ilustramos algunos perfiles gráficos que muestran que incluso para cosas muy simples, si se trata de un acceso nítido y rápido a redes como bases de datos o servidores DNS, esos servicios pueden volver mucho más rápido que el código Python puede atender a miles de esas conexiones.
fuente
Esto es lo suficientemente interesante como para analizar. Aquí hay un código para comparar el rendimiento de los greenlets versus el pool de multiprocesamiento versus multi-threading:
aquí están los resultados:
Creo que Greenlet afirma que no está obligado por GIL a diferencia de la biblioteca de subprocesos múltiples. Además, el documento de Greenlet dice que está destinado a operaciones de red. Para una operación intensiva de red, el cambio de subprocesos está bien y puede ver que el enfoque de subprocesamiento múltiple es bastante rápido. Además, siempre es preferible utilizar las bibliotecas oficiales de Python; Intenté instalar greenlet en Windows y encontré un problema de dependencia de dll, así que ejecuté esta prueba en un linux vm. Siempre trate de escribir un código con la esperanza de que se ejecute en cualquier máquina.
fuente
getsockbyname
almacena en caché los resultados a nivel del sistema operativo (al menos en mi máquina). Cuando se invoca en un DNS previamente desconocido o caducado, en realidad realizará una consulta de red, lo que puede llevar algún tiempo. Cuando se invoca en un nombre de host que se ha resuelto recientemente, devolverá la respuesta mucho más rápido. En consecuencia, su metodología de medición es defectuosa aquí. Esto explica sus resultados extraños: gevent realmente no puede ser mucho peor que el subprocesamiento múltiple; ambos no son realmente paralelos a nivel de VM.using_gevent() 421.442985535ms using_multiprocessing() 394.540071487ms using_multithreading() 402.48298645ms