¿Qué estás buscando?
Engine & platform

Inspección de la memoria con el nuevo paquete Memory Profiler

ANDY BARNARD / UNITYSoftware Engineer, Performance Tooling
Feb 28, 2023|15 minutos
Inspección de la memoria con el nuevo paquete Memory Profiler
Para tu comodidad, tradujimos esta página mediante traducción automática. No podemos garantizar la precisión ni la confiabilidad del contenido traducido. Si tienes alguna duda sobre la precisión del contenido traducido, consulta la versión oficial en inglés de la página web.

En este blog, cubriremos cinco flujos de trabajo clave en el nuevo paquete Memory Profiler que puedes usar para diagnosticar y examinar problemas relacionados con la memoria en tu juego. Estos son:

  • Monitoreo de la presión de memoria de su aplicación
  • Ver la distribución de objetos de Unity
  • Detección de activos mal configurados
  • Localización de objetos duplicados no intencionados
  • Comparación de capturas de memoria para validar optimizaciones

Para obtener una introducción al Memory Profiler, consulte el blog reciente, Todo lo que necesita saber sobre Memory Profiler 1.0.0.

Monitoreo de la presión de memoria de su aplicación

Este primer flujo de trabajo monitorea qué tan exigente es su aplicación con los recursos de memoria de un dispositivo. Este proceso es fundamental para determinar si su aplicación corre el riesgo de tener problemas de rendimiento o incluso de ser expulsada y finalizada por el sistema operativo debido a que consume demasiada memoria.

Para comenzar, tenemos una compilación de un juego de ejemplo ejecutándose en el dispositivo de destino. Naturalmente, es esencial que hagamos una captura de memoria del juego, ejecutándose en el hardware real, para ver cómo utiliza los recursos de memoria disponibles de los dispositivos. Además, la memoria no se comporta de la misma manera en el Editor de Unity que en el entorno de ejecución de Unity, por lo que tomar una captura de memoria del Editor en Modo de juego no es una buena representación de cómo se verá la memoria de un juego en un dispositivo. (Tomar una captura de memoria del Editor es apropiado cuando se desarrollan herramientas para el Editor, como ventanas del Editor personalizadas).

Después de navegar a la etapa de nuestro juego donde queremos analizar el uso de la memoria, conectamos el Memory Profiler a nuestro dispositivo usando el menú desplegable del Memory Profiler. Luego podemos tomar una captura de memoria, como se muestra a continuación.

Conectarse a una compilación de nuestro juego y tomar una captura de memoria.

Después de abrir esta captura, el Generador de perfiles de memoria muestra el uso de memoria de nuestra aplicación en la parte superior de la página Resumen como "Uso de memoria en el dispositivo".

La huella de memoria de nuestra aplicación se muestra mediante el indicador de uso de memoria en el dispositivo.
La huella de memoria de nuestra aplicación se muestra mediante el indicador de uso de memoria en el dispositivo.

Aquí podemos ver que la huella de memoria de nuestra aplicación es de 492,5 MB, de un total de 3,50 GB disponibles. A continuación, debemos utilizar nuestro mejor criterio para decidir si creemos que es una proporción sensata de la memoria física (RAM) del dispositivo a utilizar en el momento de la captura. Recuerde que la memoria física de un dispositivo es compartida por todos los procesos en ejecución.

¿Qué se entiende exactamente por “huella de memoria”?

Notarás que este indicador visual te muestra la memoriaresidente total . La memoria residente total se refiere a la cantidad de memoria de su aplicación que reside en el hardware de memoria física (RAM) del dispositivo. Este es el indicador más claro de cuán exigente es el uso actual de la memoria de su aplicación en el dispositivo de destino por dos razones. En primer lugar, esto se debe a que a medida que aumenta el uso total de memoria residente de la aplicación, también aumenta la probabilidad de incurrir en frecuentes fallas de página, donde el sistema operativo tiene que paginar memoria virtual dentro y fuera de la memoria física del dispositivo. Los errores de página frecuentes provocarán una degradación significativa del rendimiento de su aplicación. Y segundo, esto se debe a que muchos sistemas operativos utilizan el uso de la memoria residente de su aplicación para determinar su ocupación de memoriaactual. Si el uso de memoria de su aplicación es demasiado alto, el sistema operativo expulsará su aplicación y la finalizará, lo que provocará un bloqueo para sus reproductores.

Por lo tanto, puede utilizar el indicador visual de Uso de memoria en el dispositivo en el Generador de perfiles de memoria para inferir si una aplicación podría estar en riesgo de tener problemas de rendimiento o ser finalizada por el sistema operativo debido a un uso excesivo de memoria en el momento de la captura.

Esto contrasta con la memoria asignada, a veces denominada memoria comprometida, que puede notar que se muestra en varios gráficos debajo de este indicador y actualmente es la opción predeterminada que muestran todas las demás vistas, como Objetos de Unity. La memoria asignada se refiere a toda la memoria que su aplicación tiene asignada actualmente, independientemente de si se ha convertido en residente en la memoria física o no, y por lo tanto coincide más estrechamente con la visión de la memoria de su aplicación. Como tal, esto puede ser útil para explorar toda la memoria asignada actualmente a su aplicación, mientras que el uso de la memoria residente es clave para comprender la presión de memoria que su aplicación está ejerciendo sobre el hardware en cualquier momento.

Ver la distribución de objetos de Unity

La pestaña Objetos de Unity del Memory Profiler le proporciona una descripción general de la memoria de su aplicación desde la perspectiva de los objetos de Unity; es decir, las texturas, los sombreadores, las mallas, los materiales, etc. de su aplicación. Este es un excelente lugar para comenzar a explorar el Memory Profiler porque los objetos de Unity serán inherentemente familiares para muchos usuarios de Unity, ya que es con lo que la mayoría de nosotros trabajamos directamente en el Unity Editor. Esto no solo proporciona un punto de entrada familiar para comprender la memoria de nuestra aplicación, sino que también puede ayudar a diagnosticar y solucionar una variedad de problemas potenciales al proporcionar este contexto específico de Unity.

La vista de objetos de Unity.
La vista de objetos de Unity.

Para ver la vista Objetos de Unity, simplemente seleccione la pestaña Objetos de Unity en la parte superior del Generador de perfiles de memoria después de abrir una captura de memoria, como se muestra arriba.

Puede ver cómo la vista de Objetos de Unity nos brinda rápidamente una comprensión de la distribución de los tipos de objetos de Unity en nuestra aplicación. Esto nos permite obtener una comprensión de alto nivel de qué tipos consumían más memoria en el momento de la captura, así como razonar sobre esto, como por ejemplo si se espera que una escena en particular tenga muchos objetos AudioClip . Al expandir cada tipo también podemos ver cada objeto de Unity que está asignado actualmente, individualmente, como se muestra a continuación.

Podemos ver cada objeto de malla asignado actualmente por nuestra aplicación expandiendo el tipo de Malla.
Podemos ver cada objeto de malla asignado actualmente por nuestra aplicación expandiendo el tipo de Malla.

Es importante recordar que los objetos de Unity constituyen una proporción de la memoria total asignada a nuestra aplicación. Puedes ver exactamente cuánto en el indicador encima de la tabla, resaltado a continuación.

El indicador sobre la tabla nos muestra la proporción de nuestra memoria total asignada que estamos viendo en la tabla.
El indicador sobre la tabla nos muestra la proporción de nuestra memoria total asignada que estamos viendo en la tabla.

Aquí, puedes ver que nuestro tamaño total de memoria asignada, “Memoria total en instantánea”, es 4,64 GB y que nuestros objetos de Unity representan 2,37 GB de esa cantidad. Además, si filtramos la tabla (por ejemplo, utilizando la función de búsqueda), notarás que esta barra se actualiza para reflejar nuestros resultados de búsqueda. En otras palabras, muestra el tamaño de toda la memoria que se muestra actualmente en la tabla. Esto le ayuda a mantener la perspectiva de exactamente cuánta memoria está inspeccionando como proporción de la captura total y puede ayudar a informar dónde invertir los esfuerzos de optimización.

El indicador sobre la tabla se actualiza a medida que filtramos para reflejar el tamaño de la memoria que se muestra actualmente en la tabla, como proporción de la memoria total asignada.
El indicador sobre la tabla se actualiza a medida que filtramos para reflejar el tamaño de la memoria que se muestra actualmente en la tabla, como proporción de la memoria total asignada.
En la versión 1.0 de Memory Profiler, la tabla Objetos de Unity muestra la memoria asignada o, dicho de otra manera, muestra todos los objetos de Unity que están activos en su aplicación. Estamos explorando agregar visibilidad de memoria residente a estas vistas en una próxima versión, lo que le permitirá ver exactamente cuáles de sus objetos de Unity residen actualmente en la memoria física y, por lo tanto, ver exactamente cuáles contribuyen directamente a la huella de memoria actual de su aplicación.



Puede utilizar la pestaña Toda la memoria para inspeccionar el resto de la memoria de su aplicación en el momento de la captura, que incluirá memoria fuera de los objetos de Unity, como varios subsistemas de Unity, memoria solo administrada (C#) y DLL y ejecutables.
Detección de activos mal configurados

La vista de Objetos de Unity puede ayudarnos a diagnosticar una variedad de problemas potenciales. Uno de estos problemas es la detección de activos que han sido mal configurados, lo que provoca que consuman más memoria de la necesaria.

En la captura a continuación, puedes ver que una parte sustancial de nuestros objetos de Unity son texturas. La captura es de un proyecto con alta fidelidad gráfica que utiliza el High Definition Render Pipeline y hace un uso intensivo de efectos visuales. Entonces, con este contexto en mente, esperamos ver un uso intensivo de texturas, lo cual hacemos.

La cantidad de memoria asociada con los objetos de Unity en esta captura está dominada por los tipos RenderTexture y Texture2D.
La cantidad de memoria asociada con los objetos de Unity en esta captura está dominada por los tipos RenderTexture y Texture2D.

Sin embargo, al ampliar nuestra segunda gran categoría, Texture2D, podemos notar que dos texturas parecen mucho más grandes que las otras. Usando nuestro entendimiento de nuestro proyecto, nos sorprende que estas texturas sean más grandes que otras texturas comparables, como HoloTable_Normal o HoloTable_Mask, ya que esperábamos que fueran similares en tamaño.

Dos texturas en nuestra captura parecen tener el doble del tamaño de texturas comparables.
Dos texturas en nuestra captura parecen tener el doble del tamaño de texturas comparables.

Entonces, seleccionamos una de estas texturas en la tabla para conocer más detalles sobre ella y comenzar a investigar cuál podría ser la causa de esto. Aquí, en la vista Detalles, encontramos nuestra explicación: nuestra textura se puede escribir o tiene “lectura/escritura habilitada”.

La vista Detalles muestra información adicional sobre el objeto seleccionado y revela que nuestra textura está “habilitada para lectura/escritura”.
La vista Detalles muestra información adicional sobre el objeto seleccionado y revela que nuestra textura está “habilitada para lectura/escritura”.

Este es un problema común que vemos en muchos proyectos de usuarios: hacer que una textura sea escribible accidentalmente cuando no es necesario al marcar la configuración "Leer/Escribir" en la configuración de importación de la textura. Cuando una textura tiene esta bandera habilitada, duplicará su tamaño en la memoria. Esto se debe a que se requiere una segunda copia de los datos de textura para poder acceder a ellos en la CPU. Una señal reveladora de esto es que el tamaño total de una textura es el doble del tamaño esperado, o el doble del tamaño de texturas similares.

Después de deshabilitar la bandera “Leer/Escribir” en ambas texturas y tomar una segunda captura, podemos ver que ambas texturas se han reducido a la mitad en tamaño.

Las dos texturas infractoras en nuestra captura ahora tienen el mismo tamaño que las texturas comparables después del cambio.
Las dos texturas infractoras en nuestra captura ahora tienen el mismo tamaño que las texturas comparables después del cambio.
Estamos explorando agregar una columna para memoria gráfica (GPU) a la tabla de Objetos de Unity en una versión futura para facilitar la localización de casos en los que un Objeto de Unity tiene memoria gráfica asignada, como en este ejemplo.
Localización de objetos duplicados no intencionados

Un error común que vemos en los proyectos de Unity es la creación involuntaria de objetos de Unity duplicados. Por ejemplo, es muy fácil crear accidentalmente un Material duplicado al acceder a la propiedad de material de un MeshRenderer. Esto no solo se suma rápidamente en este caso (si, por ejemplo, se hace en cada instancia de un MeshRenderer en particular), sino que, además, estos materiales creados dinámicamente deben destruirse explícitamente.

Para ayudar a localizar este tipo de problema, la tabla de Objetos de Unity proporciona un filtro rápido para mostrarle únicamente los posibles Objetos de Unity duplicados. Esta vista filtrará la tabla para mostrar solo los objetos de Unity que tengan múltiples instancias con un nombre y tamaño idénticos. Es importante tener en cuenta que se esperan muchos duplicados potenciales y no es motivo de preocupación en absoluto. Por ejemplo, varias instancias de un prefabricado podrían tener componentes Transform con nombres y tamaños idénticos, y se esperaría que estos fueran duplicados. Lo único que nos interesa es descubrir duplicados no intencionales, lo cual ilustraremos en este siguiente ejemplo de flujo de trabajo.

La captura a continuación se tomó en una escena simple con dos instancias de un prefabricado de puerta, y hemos habilitado el filtro Mostrar solo duplicados potenciales ubicado debajo de la tabla Objetos de Unity. Esto ha filtrado la tabla para mostrarnos solo los objetos de Unity que tienen múltiples instancias con el mismo nombre y tamaño.

El filtro rápido “Mostrar solo duplicados potenciales” nos muestra solo objetos de Unity que tienen múltiples instancias con el mismo nombre y tamaño.
El filtro rápido “Mostrar solo duplicados potenciales” nos muestra solo objetos de Unity que tienen múltiples instancias con el mismo nombre y tamaño.

Dado que tenemos dos instancias de un prefabricado Puerta en nuestra escena, también tenemos, como se esperaba, dos instancias de todos los objetos relevantes: MeshRenderer, Transform, GameObject, etc. Sin embargo, también tenemos dos instancias del Material “Puerta” en nuestra captura anterior. Estas instancias de Puerta se ven iguales en nuestra escena, por lo que se espera que compartan un Material. Por lo tanto, se trata de un duplicado involuntario y, en este ejemplo particular, se produjo al acceder a la propiedad de material de MeshRenderer en el prefabricado. Al eliminar este acceso a la propiedad y realizar una segunda captura, se muestra que el material duplicado ya no está presente en la tabla de objetos de Unity.

El material duplicado involuntario ya no está presente en la tabla de objetos de Unity.
El material duplicado involuntario ya no está presente en la tabla de objetos de Unity.

Es importante recordar que este filtro simplemente muestra todos los objetos de Unity que tienen múltiples instancias con el mismo nombre y tamaño. Es necesario que usted conozca su proyecto para interpretar si los posibles duplicados que ve son esperados o, de hecho, no intencionales y motivo de investigación. Le recomendamos que preste atención a la barra de Memoria total en la tabla en la parte superior, que le brinda una indicación visual de qué proporción de la memoria asignada de su aplicación está viendo en la tabla. Esto puede ayudarle a mantener la perspectiva de dónde invertir sus esfuerzos de optimización.

Comparación de capturas de memoria para validar optimizaciones

El generador de perfiles de memoria también proporciona una funcionalidad para comparar dos capturas de memoria. Esto nos permite realizar cambios en nuestro proyecto, por ejemplo para abordar un problema que hayamos encontrado y, posteriormente, probar si nuestros cambios realmente tuvieron el resultado deseado. Es importante probar siempre que su hipótesis sea correcta y que sus cambios hayan tenido el resultado deseado en el hardware real. Aquí, exploremos un ejemplo de este flujo de trabajo de comparación.

A continuación se muestra una captura de nuestro juego móvil tomada durante el primer nivel. Podemos ver que la categoría más grande de objetos de Unity es Texture2D. Después de abrir esta categoría para verificar cuáles son nuestras texturas más grandes, podemos ver que hay algunas texturas de UI que son bastante grandes en relación con el resto de nuestro juego: megabytes cada una. Esto nos plantea una sospecha: ¿Por qué estas texturas son mucho más grandes que las otras y es necesario que así sea? Para descubrir por qué, primero podemos ubicar el activo de textura de origen en nuestro proyecto seleccionando la textura en el Generador de perfiles de memoria y usando el botón “Seleccionar en el editor”, que resaltará el activo de textura de origen en nuestra ventana de Proyecto.

Utilice el botón “Seleccionar en el editor” en la vista Detalles para seleccionar el activo de origen en la ventana Proyecto.
Utilice el botón “Seleccionar en el editor” en la vista Detalles para seleccionar el activo de origen en la ventana Proyecto.

Al usar la ventana del Inspector, podemos ver que todas nuestras texturas de IU grandes y problemáticas no se están comprimiendo debido a que sus dimensiones no son una potencia de dos, como lo muestra el texto “NPOT” (no es una potencia de dos).

Las seis texturas grandes no se comprimen debido a que sus dimensiones no son una potencia de dos.
Las seis texturas grandes no se comprimen debido a que sus dimensiones no son una potencia de dos.

Esto explica estos grandes tamaños de textura. Ahora podemos utilizar nuestro conocimiento de nuestro proyecto para reducir este uso de memoria. Sabemos que tres de estas texturas (los controles de ayuda) siempre se muestran juntas en la interfaz de usuario, al igual que las otras tres texturas (las criaturas). Por lo tanto, podemos plantear con gran confianza la hipótesis de que crear dos Atlas de Sprites para cada conjunto de tres texturas reducirá el uso de memoria asignada, porque permitirá comprimirlos sin aumentar la cantidad de texturas en la memoria.

Creando dos atlas de sprites para cada conjunto de tres texturas.
Creando dos atlas de sprites para cada conjunto de tres texturas.

Para comparar dos instantáneas, comience abriendo la primera instantánea. Esta es la “base” con la que queremos comparar. Ahora, encima de la instantánea abierta, seleccione la pestaña “Comparar instantáneas” y elija la segunda instantánea. El generador de perfiles de memoria ahora presentará un resumen comparando las dos instantáneas, como se muestra a continuación.

La página Resumen del Generador de perfiles de memoria presenta un resumen de las diferencias entre dos capturas de memoria cuando se comparan dos instantáneas.
La página Resumen del Generador de perfiles de memoria presenta un resumen de las diferencias entre dos capturas de memoria cuando se comparan dos instantáneas.

Para ver el efecto de nuestro cambio y verificar que, de hecho, redujo el tamaño de la memoria asignada de nuestra aplicación para la categoría Texture2D, podemos seleccionar la pestaña Objetos de Unity. Aquí, se nos presenta una tabla de comparación que muestra los tipos de objetos de Unity que han cambiado, así como también cómo han cambiado entre las capturas (que se muestran a continuación).

Los tipos de objetos de Unity que cambiaron entre las dos capturas de memoria.
Los tipos de objetos de Unity que cambiaron entre las dos capturas de memoria.

Podemos ver que nuestro tipo Texture2D en su conjunto se ha reducido en tamaño en 3,6 MB y tiene cuatro texturas menos que antes. Ampliando esta categoría, podemos ver la eliminación de nuestras texturas Sprite individuales sin comprimir y la adición de nuestras dos texturas Sprite Atlas, lo que da como resultado una reducción neta de 3,6 MB y 4 objetos Texture2D.

Los objetos de Unity Texture2D que han cambiado entre las dos capturas.
Los objetos de Unity Texture2D que han cambiado entre las dos capturas.

Así que esto fue un éxito: hemos confirmado que nuestra hipótesis era correcta utilizando la funcionalidad de comparación y hemos reducido el tamaño de estas texturas en la memoria asignada.

Conclusión

Después de leer este blog, ahora debería comprender mejor cinco flujos de trabajo clave en el nuevo paquete Memory Profiler. Estos flujos de trabajo están diseñados para diagnosticar y examinar problemas relacionados con la memoria en tu juego. Esperamos que el paquete Memory Profiler lanzado en Unity 2022.2 te ayude a monitorear, examinar y comprender mejor el uso de memoria de tu juego. No dudes en comunicarte con el equipo para compartir tus comentarios sobre cómo podemos mejorar las herramientas de creación de perfiles de rendimiento a través de nuestra página del foro , o compartir tus sugerencias a través de nuestra página de hoja de ruta, donde también puedes ver algunas de las funciones en las que se está trabajando.

Si está interesado en obtener más detalles sobre este tema, publicaremos otro blog en las próximas semanas que profundizará en cómo se calcula la huella de memoria de una aplicación, cubriendo temas como la memoria residente y asignada con más detalle.