Por qué la misma aplicación funciona de manera estable en un smartphone y falla en otro

Maxim Martynov, programador principal de una empresa internacional de juegos móviles, midió cuánto volumen de memoria reservan las diferentes arquitecturas de asignadores por encima del volumen máximo de datos activos.

Fuente de la imagen: proporcionado por el autor del material

El 19 de junio de 2026, el cofundador de Nothing, Akis Evangelidis, explicó la decisión de no lanzar el smartphone CMF Phone 3 Pro este año. No fue posible construir un modelo que pareciera un paso adelante en comparación con el anterior y que mantuviera el mismo precio debido al aumento de los precios de los chips de memoria. Este es el primer caso en que un fabricante nombra directamente el aumento de los precios de la memoria como la razón para cancelar un modelo, en lugar de posponer las fechas. Detrás de esta decisión hay un cambio más amplio: el aumento de los precios de los chips de memoria ya está presionando a todo el mercado y, especialmente, al segmento de bajo costo, donde los fabricantes se ven obligados a recortar las configuraciones de RAM. En la práctica, esto significa un escenario específico: el consumo máximo de memoria de una aplicación se ajusta al presupuesto de un dispositivo de gama alta, pero excede la cantidad que el sistema operativo puede proporcionarle en un modelo con menos RAM. Anteriormente, el ingeniero solo se enteraba de esta discrepancia después de un fallo en producción. Maxim Martynov, programador principal de Playrix con más de 10 años de experiencia en desarrollo, experto-juez de AITEX Summit Spring 2026 y jurado de Cases & Faces, propuso un método medible para esta evaluación en la etapa de diseño. Hablamos con Maxim sobre las condiciones bajo las cuales un asignador retiene memoria innecesariamente y bajo cuáles se ajusta al presupuesto físico del dispositivo.

Lo que los tests estándar no detectan

Los tests estándar de asignadores, como Larson y Google Threadtest, miden el rendimiento: la cantidad de operaciones de asignación y liberación de memoria que el sistema realiza por segundo. Sin embargo, no muestran cuánta memoria retiene el asignador por encima de lo que realmente se utiliza. Martynov se enfrentó a este problema en la práctica: en sistemas donde millones de objetos operan simultáneamente, es importante comprender cuánta memoria reserva cada arquitectura por encima de lo necesario. No tenía una herramienta lista para esta evaluación, por lo que identificó un indicador separado: la relación entre la cantidad de memoria reservada y la cantidad máxima de datos activos, que denominó coeficiente de sobrecarga. En su investigación, este indicador se utiliza junto con el rendimiento y la escalabilidad de la carga para evaluar el comportamiento de diferentes arquitecturas de asignadores. Con una memoria disponible de 4 a 6 GB, la magnitud de dicha reserva puede convertirse en una limitación arquitectónica significativa.

“Las pruebas de laboratorio y el funcionamiento de una aplicación en un dispositivo real verifican cosas diferentes. Una prueba sintética dura solo unos minutos y no muestra cómo se comporta el asignador cuando algunos objetos viven segundos y otros horas, y todo esto ocurre en la misma memoria simultáneamente. Por lo tanto, establecí el modelo de tiempo de vida del objeto como una variable experimental separada, en lugar de dejarlo como un efecto secundario”, explica Martynov.

En otras palabras, la velocidad de un asignador no es igual a su apetito por la memoria: un buen resultado en un indicador no garantiza un buen resultado en el otro.

Habiendo formalizado el coeficiente de sobrecarga, Martynov pasó a la pregunta principal: cómo se comportan las arquitecturas de asignadores reales bajo diferentes escenarios de carga. En su trabajo “Object Lifetime Models and Their Effect on Performance and Memory Fragmentation in Specialized C++ Allocators”, comparó cuatro asignadores: malloc del sistema, arena monotónica (un asignador basado en un pool de memoria de crecimiento monotónico), pool allocator (un asignador de pool para objetos de un solo tamaño) y segregated free list (un asignador con listas de bloques libres separadas por clases de tamaño). Construyó una matriz de 39 escenarios controlados en la intersección del modelo de tiempo de vida de los objetos y la naturaleza de la carga. Long-lived significa que los objetos viven mucho tiempo y se liberan masivamente; FIFO, LIFO y random describen la liberación individual en diferentes órdenes. La naturaleza de la carga define con qué trabaja el asignador: tamaño pequeño fijo, tamaño variable o rotación intensa, cuando los objetos se crean y destruyen constantemente. Por separado, Martynov probó cada asignador con un aumento de diez veces en la carga: es en este límite donde las suposiciones arquitectónicas continúan funcionando o dejan de hacerlo.

Lo que mostraron los números

Según la investigación de Maxim Martynov, en el marco del experimento, el malloc del sistema mostró el rendimiento más estable, aunque comparativamente bajo: el cambio en el modelo de tiempo de vida y la naturaleza de la carga lo afectaron menos que a las implementaciones especializadas; en los 39 escenarios, malloc se mantuvo en el rango de 0,87 a 1,07 millones de operaciones por segundo. Donde la carga corresponde a las suposiciones de una arquitectura más especializada, malloc pierde por uno o dos órdenes de magnitud.

En la misma investigación, el asignador de pool para objetos con una larga vida útil y tamaño fijo alcanza 86,4 millones de operaciones por segundo con un coeficiente de sobrecarga de 1,000×: todo el volumen reservado está ocupado por datos útiles sin residuos. Martynov registró que con un aumento de diez veces en la carga, el indicador casi no disminuye: 81,4 millones de operaciones por segundo. Esto significa que el asignador de pool no pierde eficiencia con un aumento repentino en el número de objetos activos simultáneamente en la escena o la intensidad de creación de objetos temporales, como ocurre en una escena de juego compleja. En la liberación individual de objetos (FIFO, LIFO, random), el mismo asignador muestra 46,0-49,0 millones de operaciones por segundo, por debajo del valor máximo, pero aún un orden de magnitud superior a malloc.

El tercer asignador, monotonic arena, mantiene 50,3-59,5 millones de operaciones por segundo con un coeficiente de sobrecarga fijo de 1,25×. En la configuración investigada, monotonic arena se utilizó en escenarios donde los objetos se liberan en grupo: no maneja liberaciones individuales, y la memoria ocupada solo se devuelve cuando se reinicia toda la arena. Una reserva pequeña pero constante surge debido a la política de crecimiento de bloques, cuando el bloque actual se llena y el asignador asigna uno nuevo, dejando parte del último sin usar.

Martynov obtuvo el resultado más inesperado con la lista de bloques libres segregados (segregated free list). Con una rotación intensa de objetos, cuando los datos activos son solo 8729 bytes, el asignador mantiene en reserva más de 2 millones de bytes: el coeficiente de sobrecarga alcanza 236,5×. Con un tamaño variable de objetos, los gastos generales se mantienen en 7,6-8,0× con un rendimiento de 31,2-35,5 millones de operaciones por segundo. En escenarios con objetos de larga vida, el coeficiente, por el contrario, cae por debajo de 1× y llega a 0,08×, pero esto no es un signo de eficiencia: el volumen total de objetos vivos excede la capacidad de las clases de tamaño preasignadas, y el asignador entra en modo de falla de emergencia en lugar de ahorrar memoria. Con un aumento de diez veces en la carga, el sistema entra en modo de emergencia: 2.002.194 eventos de agotamiento de la capacidad de las clases de tamaño. Como señala el especialista, la razón es que en la implementación investigada, la lista segregada de bloques libres preasigna agresivamente las clases de tamaño: con una carga adecuada, esto proporciona velocidad; si no coincide, desperdicia memoria o conduce a una denegación de servicio.

“Antes de elegir un asignador, el equipo debe fijar la distribución de tamaños, el rango de tiempo de vida de los objetos, el orden de liberación y la intensidad máxima de rotación. Estos cuatro parámetros permiten evaluar de antemano la reserva de memoria y el riesgo de agotamiento de las clases de tamaño”, comenta Maxim Martynov.

Ninguno de los cuatro asignadores gana en todos los escenarios a la vez: la elección siempre depende de la carga específica. La novedad de la metodología de Maxim Martynov es que el modelo de tiempo de vida de los objetos se utiliza como una variable experimental independiente y se compara con el rendimiento, la reserva de memoria y la escalabilidad de la carga. Esto proporciona a los ingenieros una forma reproducible de verificar la arquitectura con la carga de antemano.

Qué significa esto para la industria

La crisis de precios de la memoria no cambia la matemática misma de cómo funcionan los asignadores: era la misma hace uno o tres años. Lo que cambia son las consecuencias que se hacen evidentes. Mientras un dispositivo tenía 12-16 gigabytes de memoria, la brecha entre el volumen reservado y el realmente utilizado seguía siendo un detalle que podía pasarse por alto. Con 4-6 gigabytes, se convierte en una limitación: una aplicación que técnicamente funciona con la memoria completa puede no caber en un dispositivo reducido porque el asignador mantiene parte de la memoria vacía como reserva, y esta reserva no encaja en el presupuesto reducido. La elección de la arquitectura del asignador deja de ser un detalle de optimización y se convierte en parte de la especificación del volumen de memoria disponible para el producto. En la práctica, esto significa que un ingeniero puede verificar la arquitectura del asignador con el presupuesto de memoria del dispositivo en la etapa de diseño, y no después de que los usuarios de smartphones económicos comiencen a quejarse de bloqueos y cierres inesperados de la aplicación. Para una industria donde el aumento de los precios de los componentes ya está cambiando las líneas de productos, la metodología de Martynov proporciona una forma reproducible de verificar la arquitectura del asignador de antemano, y no a posteriori, cuando la aplicación deja de caber en el presupuesto de memoria del dispositivo.