9 puntos por GN⁺ 2025-06-14 | 2 comentarios | Compartir por WhatsApp
  • Un texto retrospectivo escrito por el propio Jason Evans, quien lideró el desarrollo de jemalloc, que repasa unos 20 años de trayectoria del proyecto en 5 etapas y documenta con honestidad sus éxitos, fracasos, los límites de un proyecto de código abierto y su declive por cambios internos en la empresa
  • El asignador de memoria jemalloc comenzó en 2004 y fue ampliamente usado durante unos 20 años, pero recientemente su desarrollo oficial se detuvo debido a cambios internos en Meta
  • Acumuló experiencia en optimización de rendimiento y portabilidad a través de varias etapas, como su integración inicial en FreeBSD, la resolución de problemas de rendimiento en Firefox y su adopción a gran escala en Facebook
  • A lo largo de diversos desafíos, como los problemas de fragmentación del almacenamiento y de escalabilidad, se añadieron múltiples funciones, incluyendo mejoras de rendimiento, recolección de estadísticas e infraestructura de pruebas
  • Con el cambio en la cultura corporativa dentro de Meta, el descenso en la inversión técnica y la ausencia de mantenedores clave llevaron a la interrupción del desarrollo a largo plazo
  • Factores como la eliminación de Valgrind y la falta de retroalimentación de usuarios externos actuaron como límites estructurales dentro del ecosistema de código abierto
  • En adelante sigue abierta la posibilidad de nueva evolución mediante un fork, pero no se espera que el desarrollo principal oficial continúe

Resumen de jemalloc

  • jemalloc es un asignador de memoria de código abierto concebido por primera vez en 2004, desarrollado con el objetivo de mejorar el rendimiento y la escalabilidad, y utilizado activamente durante 20 años en importantes proyectos de código abierto e infraestructura de grandes tecnológicas
  • Como el desarrollo principal se ha detenido recientemente, en adelante se espera mantenimiento mediante forks o por parte de organizaciones individuales

Phase 0: Lyken

  • En 2004 comenzó como un asignador de memoria manual durante el desarrollo de un lenguaje de programación para computación científica llamado Lyken
  • El asignador interno de Lyken quedó funcionalmente completo en mayo de 2005
  • Más tarde, al integrarse en FreeBSD, fue eliminado de Lyken y solo quedó un contenedor delgado sobre el asignador del sistema
  • La razón de su eliminación fue que, tras integrarse en FreeBSD, quedó más claro qué carencias tenía el asignador del sistema, como el seguimiento de asignaciones por hilo
  • Curiosamente, más adelante se añadieron a jemalloc las funciones de recolección de estadísticas que se necesitaban en la época de Lyken

Phase 1: FreeBSD

  • En 2005, cuando las computadoras multiprocesador se estaban volviendo comunes, FreeBSD usaba phkmalloc, pero no era adecuado para entornos con hilos paralelos
  • Como el asignador de Lyken tenía ventajas claras en escalabilidad, se integró en FreeBSD y pronto recibió el nombre de jemalloc
  • Sin embargo, surgieron graves problemas de fragmentación en aplicaciones como KDE, lo que puso en duda su viabilidad
  • La causa de la fragmentación era un método de asignación en un espacio unificado sin separación por tamaño, y tras investigarlo se rediseñó a fondo la estructura con un algoritmo de separación por regiones según tamaño
  • El contenido de este proceso quedó documentado en un artículo de BSDCan de 2006

Phase 1.5: Firefox

  • En 2007, antes del lanzamiento de Mozilla Firefox 3, la fragmentación de memoria era un problema importante en Windows
  • Portar jemalloc a Linux fue fácil, pero Windows fue complicado
  • Mozilla hizo un fork de jemalloc almacenado en la libc de FreeBSD para mejorar portabilidad y compatibilidad
  • Con el tiempo, Mozilla aportó muchas mejoras al upstream de jemalloc, pero la versión fork siempre mostró mejor rendimiento
  • No está claro si esto se debía a regresiones de rendimiento o a optimizaciones ajustadas a un entorno específico

Phase 2: Facebook

  • Cuando ingresó a Facebook en 2009, el mayor obstáculo de jemalloc era la falta de herramientas, como profiling y detección de fugas
  • Para resolverlo, se introdujo en jemalloc 1.0.0 la función de heap profiling compatible con pprof
  • Tras mover el desarrollo a Github, junto con usuarios y contribuidores externos se realizaron muchas mejoras, como infraestructura de pruebas, soporte para Valgrind, estadísticas JSON y nueva gestión de páginas
  • Internamente, el enorme sistema de telemetría de Facebook ayudó mucho a la optimización del rendimiento y a prevenir regresiones
  • 3.x: introducción de infraestructura de pruebas y soporte para Valgrind
  • 4.x: se añadió decay-based purging y estadísticas JSON
  • 5.x: cambio de un diseño basado en chunk a uno basado en extent, preparando el uso de huge pages de 2MiB
  • El análisis de rendimiento basado en telemetría dentro de Facebook desempeñó un papel decisivo en la optimización
  • La eliminación del soporte para Valgrind en la versión 5.0.0 se decidió porque internamente no se usaba, pero hubo una fuerte reacción externa, incluso de desarrolladores de Rust
  • Después, debido a cambios organizacionales en Facebook/Meta, el equipo de jemalloc se redujo y la estrategia del negocio pasó de la inversión en tecnología clave a un enfoque en eficiencia
  • Como resultado, el desarrollo de funciones grandes como Huge Page Allocation se estancó, y algunos trabajos quedaron inconclusos
  • Tras la salida de Evans en 2017, Qi Wang mantuvo el desarrollo durante varios años
  • Incluso después del relevo en el liderazgo del equipo, varios contribuidores siguieron manteniendo el proyecto, pero dejó de haber una persona responsable de la visión a largo plazo

Phase 4: Stasis (estancamiento)

  • En la actualidad, el desarrollo upstream de jemalloc ha terminado, y Meta también sigue una dirección aparte según sus necesidades internas
  • La deuda técnica de la base de código existente es tan grande que primero sería necesario un refactor importante
  • Los requisitos de Facebook/Meta y los de los usuarios externos ya no coinciden
  • Si se reanuda el desarrollo en el futuro, primero habría que dedicar cientos de horas a limpiar la deuda técnica, y el autor no tiene motivación para hacerlo
  • Es posible hacer un fork externo a partir de la rama dev o de la versión 5.3.0, así que siempre existe la posibilidad de que aparezca un nuevo proyecto basado en un fork

Reflexiones y lecciones

  • El conflicto generado por la eliminación del soporte para Valgrind se originó en la falta de conocimiento sobre los casos de uso externos
  • Incluso el hecho de que jemalloc se usaba en Android se supo solo dos años después
  • Aunque el proyecto estaba completamente abierto en GitHub, los contribuidores clave de organizaciones externas no lograron mantenerse en el tiempo
    • Tanto Mike Hommey de Firefox como el intento de migrar a CMake quedaron inconclusos
  • Según la experiencia del autor, simplemente abrir un proyecto no lo convierte en un proyecto independiente y sostenible
  • Se enfatiza que el código abierto no se mantiene solo por estar publicado, y que es esencial cultivar contribuidores clave y asegurar gobernanza

Comentario final

  • jemalloc fue una experiencia especial incluso para un autor que había sido partidario del garbage collection durante más de 25 años
  • Aunque ahora vuelve a concentrarse en desarrollar sistemas con garbage collection, expresa un profundo agradecimiento a todas las personas que colaboraron con jemalloc

2 comentarios

 
ganadist 2025-06-14

También hay una traducción del artículo completo.
https://rosettalens.com/s/ko/jemalloc-postmortem

 
GN⁺ 2025-06-14
Comentarios en Hacker News
  • Entiendo la decisión de archivar el repositorio upstream. Antes de irme de Meta, nuestro equipo de Jemalloc no tenía capacidad para responder a todo tipo de issues que llegaban a GitHub (por ejemplo, una vez alguien abrió un issue porque las pruebas no pasaban en Itanium, y para mí fue un caso un poco gracioso). Aun así, me da tristeza ver esta situación. Incluso ahora, sigo sintiendo que jemalloc es la opción con mejor rendimiento y más fácil de usar entre las implementaciones de malloc de propósito general. TCMalloc también es excelente, pero creo que es realmente difícil de usar si no utilizas bazel (hoy en día bazel 7.4.0 agregó cc_static_library, así que exportarlo como librería estática es un poco más fácil, pero ese punto sigue siendo importante). Tengo pensado preguntarle a Qi si podría hacer un último release 6.0 antes de volver a archivar el repositorio. Si va a ser el último release, creo que estaría bien modernizar un poco la configuración por defecto. Por ejemplo, una gran mejora sería desactivar por defecto la opción cache oblivious, que pese al nombre resulta confusa y hace que la size-class de 16 KiB se infle innecesariamente a 20 KiB. No busco criticar la elección anterior (es decir, la decisión inicial de Jason); cuando lo hablamos en ese momento con Qi y David, había una razón entendible: en general la associativity del TLB era mucho más baja que ahora. En la misma línea, también sería un buen cambio subir el page size por defecto de 4 KiB a un valor mayor (algo como 16 KiB). Eso mueve el umbral de las size-classes grandes —el punto donde se deja de asignar varias asignaciones dentro de un slab y se pasa a asignarlas en rangos individuales— de 16 KiB a 64 KiB, y el efecto es grande. Antes de irme de Meta, revisé aplicar este cambio a un servicio interno importante como último ajuste, y era una optimización que reducía el uso de CPU en varios puntos porcentuales a cambio de un pequeño aumento de memoria por fragmentación de RAM. También hay otras cosas que me gustaría cambiar (por ejemplo, cambiar el valor por defecto de metadata_thp de disabled a auto, o modificar el dimensionamiento de extents de los slabs para reducir la fragmentación aceptando cerca de un 1% de desperdicio en lugar de exigir múltiplos exactos del tamaño de página). Aun así, las configuraciones mencionadas antes serían los cambios más grandes

    • La persona que abrió el issue por la falla del test suite en Itanium fui yo

    • Este tipo de experiencias reales e insights de alguien interno son justo la razón por la que sigo entrando a Hacker News. Me da curiosidad por qué TCMalloc es difícil de usar sin bazel (lo pregunto genuinamente)

    • Ojalá este conocimiento interno tan importante se publicara en materiales más amplios, como documentación oficial o entradas de blog. En este momento, la documentación oficial se siente demasiado escasa. Sería bueno que la experiencia acumulada de tanto trabajo hecho dentro de Meta se compartiera antes de que se pierda con el tiempo

    • Da un poco de pena que, a pesar de ser un software excelente, no se aproveche bien por lo complejo que es su proceso de build e integración

    • Mencionaste la parte de que “el equipo de Jemalloc no tenía suficiente capacidad para atender issues aleatorios en GitHub”, y me da curiosidad saber por qué la gestión de issues no funcionaba con fluidez aun cuando Meta aparentemente tenía suficiente gente a cargo del proyecto. Si estoy entendiendo mal la situación, corrígeme por favor

  • Quiero contar cuánto ha impactado el trabajo de Jason en nuestro negocio. Nuestra empresa es bastante grande y procesa cientos de millones de imágenes y videos al día. Durante los primeros años sufrimos muchísimo por problemas de fragmentación de memoria. Entonces un día adoptamos jemalloc, cambiamos literalmente solo dos líneas en el Dockerfile, y todos los problemas desaparecieron. Hoy somos una empresa que factura decenas de miles de millones y usamos jemalloc en todos nuestros servicios y Dockerfiles. De verdad, muchísimas gracias de corazón

    • En la práctica, muchos servicios de procesamiento de imágenes basados en golang recomiendan o usan jemalloc. Entre los 3 principales del tema resize-images al 2025-06-13, imaginary lo usa así en su Dockerfile, y en el caso de imgproxy se discute incluso dentro del repo de imaginary tomando como referencia documentación archivada. Imagor también está usando jemalloc en esta parte

    • Lo pregunto con genuina curiosidad (sin sarcasmo): ¿también hiciste una donación? Siento que expresar gratitud con dinero es la mejor forma de agradecer

  • Sobre la opinión de que “jemalloc fue retirado de los binarios de Rust más rápido de lo esperado”, quiero compartir que en realidad ese issue fue solo una de varias causas. En este comentario se puede ver el contexto. Y jemalloc no fue eliminado por completo sino hasta dos años después de que se planteó ese issue por primera vez (referencia: este PR)

    • Entre los varios issues mencionados ahí, me parece interesante que el problema del tamaño de página hardcodeado en arm64 siga sin resolverse upstream hasta hoy. Por culpa de eso, los desarrolladores de apps tienen que distribuir varios binarios distintos para arm64 Linux o abandonar soporte para algunas plataformas. Me pregunto si, de haberse introducido un tamaño de página dinámico (aplicando parches binarios dinámicos al estilo ftrace por rendimiento), habría sido mucho más lento que ahora
  • Se volvió una costumbre para mí usar jemalloc en todos los motores de juego que he hecho durante años. En win32 es mucho más rápido que el asignador por defecto, y además tiene la ventaja de usar el mismo asignador en todas las plataformas. Lo conocí cuando se integró en FreeBSD, y desde entonces he usado solo jemalloc. Me enorgullece pensar que, gracias a jemalloc, muchísimos jugadores han podido disfrutar mis juegos

    • El allocator por defecto de Windows sí que es bastante malo. Jemalloc es lo máximo
  • Me pareció un gran texto — me pregunto si Facebook (ahora Meta) ya no usa jemalloc como tal, o si solo está en fase de mantenimiento. También tengo la duda de si quizá migraron a tcmalloc u otro allocator. Había una frase que decía: “En la ingeniería de infraestructura de Facebook, el énfasis se desplazó de invertir en tecnología base a priorizar el ROI”

    • Me fui de Meta hace casi dos años (y no creo que haya cambiado demasiado), pero jemalloc sigue usándose con enlace estático en todos los binarios dentro de Meta. Sobre si sería fácil cambiar a tcmalloc u otro allocator, la respuesta es que jemalloc está acoplado muy profundamente al ecosistema interno de la empresa, así que cambiarlo es más difícil de lo que parece. Desde el plumbing de telemetría de Strobelight, pasando por muchas extensiones usadas específicamente con jemalloc (por ejemplo, arenas manuales que usan custom extent hooks directamente), hasta el hecho de que la mayoría de las aplicaciones realmente evolucionaron para exprimir al máximo las características de jemalloc, todo está entrelazado

    • Un cambio grande reciente es que todos los mantenedores de largo plazo de jemalloc ya se fueron. Pero, paradójicamente, dentro de Facebook ahora hay más interés por el proyecto que antes, y después de algunos issues polémicos recientes, tengo una visión positiva de que ojalá se avance en una dirección que tome en cuenta tanto a Qi y Jason como a los usuarios externos

    • Meta sigue desarrollando activamente su fork propio aquí

  • Fue un honor para mí haber podido formar parte de este trabajo tan influyente a lo largo del tiempo, desde Firefox hasta Facebook

    • También quiero dejar aquí mi agradecimiento en el momento adecuado. No esperaba que este texto se publicara hoy, pero fue un honor haber podido formar parte, aunque sea de una pequeña parte, de una gran travesía. También quiero agradecer a @je, qi, david y a quienes contribuyeron en esa época y después

    • Creo que tu liderazgo, impulsando que Facebook siguiera invirtiendo en tecnologías base, dio tantos frutos como era posible. Innovaciones como GraphQL, PyTorch y React fueron posibles gracias a esa base

  • La cita de FTA, “Ante opciones imposibles, la gente solo puede 1) tomar malas decisiones bajo una presión extrema, 2) conformarse bajo una presión extrema, o 3) buscar una salida lateral”, me parece una descripción difícil de imaginar como ambiente laboral

    • Me da amargura decir que casi todos los lugares donde he trabajado desde 2008 eran así
  • Creo que jemalloc es el único allocator dentro de macOS que puede sobrescribir malloc/free de forma fluida, como si fuera LD_PRELOAD (al menos alrededor de 2020). Puede meterse fácilmente como allocator por defecto mediante el enfoque basado en zones, y además encaja bien con los requisitos particulares de allocator de Apple. Otros allocators de terceros solían fallar por esas exigencias

    • Eso sí, este enfoque solo puede funcionar bajo la suposición de que el allocator del sistema de macOS no cambie sus estructuras internas, y recuerdo que Apple sí hizo cambios que lo rompieron al menos un par de veces

    • Creo que mimalloc también puede funcionar de la misma manera, aunque no estoy seguro

  • Tengo entendido que jemalloc mejora frente al malloc de glibc prácticamente en todos los aspectos, y que en benchmarks siempre muestra mejor rendimiento, así que como alguien de afuera siempre me he preguntado por qué no es el allocator por defecto

    • En FreeBSD ya es el predeterminado. Si quieres cambiar el malloc, probablemente sea más fácil cambiar también libc por la de FreeBSD, y si haces eso, lo natural sería subir también el kernel a FreeBSD. Cuando nos fusionamos con Facebook, yo estaba emocionado de presentarle jemalloc a gente de nuestro lado, pero como ya usábamos FreeBSD, para ellos era algo totalmente normal

    • No soy ingeniero de allocators, así que no es una opinión experta, pero hace tiempo hablé con un ingeniero que mantenía el allocator del sistema operativo. Según él, los allocators personalizados tienden a darle una gran ventaja a un proceso concreto en asignación de memoria, pero empeoran la equidad global de asignación del sistema. Desde la perspectiva del allocator del sistema, si cada proceso tiene patrones distintos, optimizar el conjunto se vuelve más difícil. Por eso en la mayoría de los entornos de servicios jemalloc se recomienda tanto: parte de la idea de que “lo único que importa ahora es mi proceso”

    • No creo que haya una razón técnica por la que jemalloc no pueda ser el allocator por defecto. De hecho, ya lo es en FreeBSD (como menciona el artículo). Según entiendo, este tema es más político que técnico

    • Jemalloc ya fue probado en producción a gran escala, tiene una licencia muy permisiva y su rendimiento está demostrado. Entonces, más allá de la “pureza ideológica” o la inercia del legado, sinceramente me pregunto quién gana con seguir aferrado al malloc de glibc. Dan ganas de preguntar por qué todavía siguen escudándose en la “compatibilidad” como excusa

    • Antes había un problema importante con allocators alternativos: nunca devolvían al sistema operativo la memoria liberada y solo la retenían como dirty pages (eso al final mejoró, pero muestra claramente las diferencias de prioridad entre allocators). Además, en realidad la mayoría de los procesos solo ejecutan un hilo principal o tienen hilos casi inactivos. Los allocators optimizados para multithreading pueden ser excesivos y una carga innecesaria en esos entornos. También quiero mencionar que mucha gente no considera que el costo de que el kernel ponga páginas en zero, o que un proceso de usuario las ponga en zero para reutilizarlas internamente, en la práctica no difiere tanto

  • Más bien me gustaría que agregaran el enlace a la entrada del blog al repositorio de GitHub. Creo que es importante que quienes visiten el repo en el futuro tengan este contexto y lo puedan consultar