Los primeros intentos de eliminar Python GIL dieron como resultado un mal rendimiento: ¿por qué?

13

Esta publicación del creador de Python, Guido Van Rossum, menciona un intento temprano para eliminar la GIL de Python:

  

Esto se ha intentado antes, con resultados decepcionantes, por lo que   Soy reacio a poner mucho esfuerzo en ello yo mismo. En 1999 Greg Stein   (¿Con Mark Hammond?) produjo una bifurcación de Python (1.5 creo) que   quitó la GIL, reemplazándola con cerraduras de grano fino en todos los mutables   estructuras de datos. También presentó parches que eliminaron muchos de los   dependencias en estructuras de datos mutables globales, que acepté.   Sin embargo, después de la evaluación comparativa, se demostró que incluso en la plataforma   con la primitiva de bloqueo más rápida (Windows en el momento) se desaceleró   ejecución de un solo hilo hacia abajo casi el doble, lo que significa que en dos   CPU, usted podría hacer un poco más de trabajo sin la GIL que   en una sola CPU con el GIL. Esto no fue suficiente, y el parche de Greg   Desapareció en el olvido. (Vea la reseña de Greg sobre la actuación).

Apenas puedo discutir los resultados reales, pero realmente me pregunto por qué sucedió esto. Presumiblemente, la razón principal por la que eliminar el GIL de CPython es tan difícil es el sistema de gestión de memoria de conteo de referencias. Un programa típico de Python llamará Py_INCREF y Py_DECREF miles o millones de veces, por lo que es un punto clave de contención si Teníamos que envolver las cerraduras a su alrededor.

Pero, no entiendo por qué agregar primitivos atómicos ralentizaría un programa de single . Supongamos que solo modificamos CPython para que la variable refcount en cada objeto de Python fuera una primitiva atómica. Y luego simplemente hacemos un incremento atómico (instrucción de búsqueda y adición) cuando necesitamos incrementar el recuento de referencias. Esto haría que el recuento de referencias de Python fuera seguro para subprocesos, y no debería tener ninguna penalización de rendimiento en una aplicación de un solo subproceso, porque no habría contención de bloqueo.

Pero, por desgracia, muchas personas que son más inteligentes que yo lo han intentado y han fallado, por lo que obviamente me estoy perdiendo algo aquí. ¿Qué hay de malo en la forma en que veo este problema?

    
pregunta Siler 04.07.2014 - 00:25

2 respuestas

9

No estoy familiarizado con la bifurcación de Greg Stein Python, así que descarte esta comparación como analogía histórica especulativa si lo deseas. Pero esta fue exactamente la experiencia histórica de muchos bases de código de infraestructura que se trasladaron desde implementaciones de un solo subproceso múltiple.

Esencialmente, todas las implementaciones de Unix que estudié en la década de 1990 (AIX, DEC OSF / 1, DG / UX, DYNIX, HP-UX, IRIX, Solaris, SVR4 y SVR4 MP) pasaron exactamente por este tipo de " ponemos el bloqueo de grano fino, ahora es más lento ". problema. Los DBMS que seguí (DB2, Ingres, Informix, Oracle y Sybase) también lo hicieron.

He escuchado "estos cambios no nos ralentizarán cuando ejecutemos un solo hilo" un millón de veces. Nunca funciona de esa manera. El simple acto de verificar condicionalmente "¿estamos ejecutando multiproceso, o no?" agrega una sobrecarga real, especialmente en las CPU altamente canalizadas. Las operaciones atómicas y los bloqueos de giro ocasionales agregados para garantizar la integridad de las estructuras de datos compartidos deben llamarse con bastante frecuencia y son muy lentos. Las primitivas de bloqueo / sincronización de primera generación también fueron lentas. La mayoría de los equipos de implementación eventualmente agregan varias clases de primitivas, en varias "fortalezas", dependiendo de cuánta protección de interbloqueo fue necesaria en varios lugares. Luego, se dieron cuenta de que, al principio, abofetearon a los primitivos de bloqueo, no era realmente el lugar correcto, por lo que tuvieron que perfilar, diseñar alrededor de los cuellos de botella encontrados y sistemáticamente rotar. Algunos de estos puntos conflictivos finalmente obtuvieron la aceleración del sistema operativo o del hardware, pero toda la evolución tomó de 3 a 5 años, como mínimo. Mientras tanto, las versiones MP o MT fueron cojeando, en cuanto al rendimiento.

De lo contrario, los equipos de desarrollo sofisticados han argumentado que tales desaceleraciones son básicamente un hecho de la vida persistente e intratable. IBM por ejemplo se negó a habilitar SMP para AIX durante al menos 5 años después de la competencia, insistiendo en que el solo hilo era simplemente mejor. Sybase utilizó algunos de los mismos argumentos. La única razón por la que algunos de los equipos finalmente llegaron fue que el rendimiento de un solo hilo ya no podía mejorarse razonablemente a nivel de CPU. Fueron obligados a ir MP / MT o aceptar tener un producto cada vez menos competitivo.

La concurrencia activa es DIFÍCIL. Y es engañoso. Todo el mundo se apresura a pensar que "esto no será tan malo". Luego golpean las arenas movedizas, y tienen que seguir adelante. He visto que esto suceda con al menos una docena de equipos de marca reconocidos, bien financiados e inteligentes. En general, parece que tardan al menos cinco años después de elegir multiproceso para "volver a donde deberían estar, en cuanto al rendimiento" con los productos MP / MT; la mayoría seguía mejorando significativamente la eficiencia / escalabilidad de MP / MT incluso diez años después de hacer el cambio.

Así que mi especulación es que, sin el respaldo y apoyo de GvR, nadie ha asumido el largo camino de Python y su GIL. Incluso si fuesen a hacerlo hoy, sería Python 4.x el período de tiempo antes de que dijeras "¡Guau! ¡Estamos realmente sobre la joroba de MT!"

Tal vez haya algo de magia que separe a Python y su tiempo de ejecución del resto del software de infraestructura con estado: todos los tiempos de ejecución del idioma, los sistemas operativos, los monitores de transacciones y los administradores de bases de datos anteriores. Pero si es así, es único o casi. Todos los demás que eliminaron un equivalente de GIL han tomado más de cinco años de esfuerzo e inversión ardua y comprometida para obtener de MT-not to MT-hot.

    
respondido por el Jonathan Eunice 09.07.2014 - 04:18
-1

Otra hipótesis descabellada: en 1999, Linux y otros Unices no tenían una sincronización eficaz como la que tiene ahora con futex(2) ( enlace ). Estos se produjeron alrededor de 2002 (y se fusionaron en 2.6 en 2004).

Dado que todas las estructuras de datos integradas tienen que estar sincronizadas, el bloqueo cuesta mucho. Ya señalamos, que las operaciones atómicas no son necesarias baratas.

    
respondido por el Sahib 04.07.2014 - 23:10

Lea otras preguntas en las etiquetas