- Instagram introdujo Objetos Inmortales – PEP-683 – a Pitón. Ahora, los objetos pueden pasar por alto las verificaciones de contadores de referencia y vivir durante el tiempo de ejecución, lo que abre oportunidades emocionantes para una verdadera concurrencia.
En Meta, estamos usando Python (Django) para nuestro servidor externo de Instagram. Para manejar la concurrencia, confiamos en una arquitectura multiprocesador junto con asyncio para la concurrencia por proceso. Sin embargo, nuestra escala, tanto en términos de lógica comercial como en términos del volumen de consultas que procesamos, puede generar una mayor presión de la memoria, lo que resulta en una menor eficiencia.
Para mitigar este efecto, confiamos en la arquitectura del servidor web anterior a la bifurcación para almacenar en caché tantos objetos como sea posible y hacer que cada proceso individual los use como una estructura de solo lectura a través de la memoria compartida. Si bien esto ayuda mucho, tras una inspección más cercana, vimos que el uso de la memoria propia de nuestros procesos creció con el tiempo, mientras que nuestra memoria compartida disminuyó.
Después de analizar el montón de Python, descubrimos que, si bien la mayoría de nuestros objetos de Python eran esencialmente inmutables y existían durante el tiempo de ejecución, finalmente se modificaron mediante el conteo de referencias y las operaciones de recolección de elementos no utilizados (GC) que modifican los objetos. metadatos en cada ciclo de recolección de basura y lectura, lo que desencadena Copiar en escrito en el proceso del servidor.
El efecto de copiar en escritura es aumentar la memoria privada y disminuir la memoria compartida del proceso principal.
Objetos inmortales para Python
Este problema de cambiar el estado de los objetos compartidos es el núcleo de cómo funciona el tiempo de ejecución de Python. Dado que se basa en el conteo de referencias y la detección de ciclos, el tiempo de ejecución requiere un cambio en la estructura de memoria subyacente del objeto, que es una de las razones por las que el lenguaje requiere un bloqueo de intérprete global (GIL).
Para solucionar este problema, presentamos Objetos inmortales – PEP-683. Esto crea un objeto inmortal (un objeto para el cual el estado del objeto subyacente nunca cambiará) al marcar un valor especial en el campo de conteo de referencia del objeto. Esto permite que el tiempo de ejecución sepa cuándo puede y cuándo no puede cambiar los campos de recuento de referencias y el encabezado del GC.
Comparación de objetos estándar con objetos inmortales. Con los objetos estándar, el usuario puede asegurarse de no cambiar su tipo y/o sus datos. Immortality agrega una garantía adicional de que el tiempo de ejecución no cambiará el recuento de referencias o el encabezado GC si está presente, lo que garantiza que el objeto sea completamente inmutable.
Si bien implementar y lanzar esto en Instagram fue un proceso relativamente simple debido a nuestro entorno relativamente aislado, llevar esto a la comunidad fue un proceso largo y difícil. Gran parte de esto se debió implementación de la soluciónque tuvo que lidiar con una serie de problemas, como la compatibilidad con versiones anteriores, la compatibilidad de la plataforma y la degradación del rendimiento.
En primer lugar, la implementación debía garantizar que, incluso después de cambiar la implementación del recuento de referencias, las aplicaciones no fallarían si algunos objetos recibían repentinamente valores de recuento de referencias diferentes.
En segundo lugar, cambia la representación de la memoria principal del objeto de Python y cómo incrementa el recuento de referencias. Tenía que funcionar en todas las plataformas diferentes (Unix, Windows, Mac), compiladores (GCC, Clang y MSVC), arquitecturas (32 bits y 64 bits) y tipos de hardware (little endian y big endian).
Finalmente, la implementación base se basa en la adición de controles explícitos en las rutinas de incremento y decremento del conteo de referencias, que son las dos rutas de código más populares en toda la ejecución del tiempo de ejecución. Esto inevitablemente significó una disminución en el rendimiento del servicio. Afortunadamente, mediante un uso juicioso de la asignación de registros, pudimos reducir la regresión a solo ~2 % por sistema, lo que hace que la regresión sea razonable por los beneficios que brinda.
Cómo Immortal Objects impactó Instagram
Para Instagram, nuestro objetivo inicial era mejorar la memoria y el uso de la CPU de nuestras solicitudes al reducir la cantidad de copias en la publicación. Con la ayuda de objetos inmortales, pudimos reducir significativamente la memoria privada al aumentar el uso de la memoria compartida.
Aumentar el uso de memoria compartida con objetos inmortales nos permite reducir significativamente la memoria privada. Reducir el número de copias al grabar.
Sin embargo, las implicaciones de estos cambios van mucho más allá de Instagram y afectan la evolución de Python como lenguaje. Hasta ahora, una de las limitaciones de Python ha sido que no puede garantizar que los objetos del montón sean realmente inmutables. Tanto el recolector de elementos no utilizados como el motor de recuento de referencias tenían acceso sin restricciones a estos dos campos.
Agregar objetos inmortales a Python proporciona verdaderas garantías de inmutabilidad por primera vez. Esto ayuda a los objetos a eludir los contadores de referencia y las verificaciones de recolección de elementos no utilizados. Esto significa que ahora podemos compartir objetos inmortales entre subprocesos sin necesidad de que GIL proporcione seguridad para subprocesos.
Este es un bloque de construcción importante para el tiempo de ejecución de Python de múltiples núcleos. Hay dos oraciones que usan objetos inmortales de diferentes maneras para lograr esto:
- PEP-684: GIL por intérprete
- PEP-703: Hacer que el bloqueo del intérprete global sea opcional en CPython
Prueba Immortal Objects hoy
Invitamos a la comunidad a pensar en cómo pueden usar la inmortalidad en sus aplicaciones, así como a considerar las propuestas existentes para anticipar cómo mejorar sus aplicaciones para un entorno multinúcleo. En Meta, estamos entusiasmados con la dirección que está tomando el lenguaje y estamos listos para continuar contribuyendo desde afuera a medida que continuamos experimentando y haciendo crecer Instagram.