Historias de las trincheras de la optimización: Mejor gestión de la eliminación de código con Unity 2020 LTS

ALEXANDER DEGTYAREV / UNITY TECHNOLOGIESSenior Software Development Consultant
Oct 25, 2021|10 minutos
Historias de las trincheras de la optimización: Mejor gestión de la eliminación de código con Unity 2020 LTS
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.

La eliminación de código gestionado es un paso fundamental en el proceso de compilación que ayuda a reducir el tamaño de los archivos binarios de una aplicación. Esto se consigue eliminando el código que no se utiliza.

Al eliminar código, te aseguras de que no se compilará ni se incluirá en la compilación final. Aunque esto debería reducir ligeramente el uso de memoria en los proyectos que se ejecutan con el backend IL2CPP, siempre existe el riesgo de que se pierdan tipos y métodos en tiempo de ejecución (entre otros problemas) con niveles más altos de eliminación de código gestionado.

A lo largo del proceso de compilación, parte del código se considera no utilizado y, en consecuencia, se reduce. Añadir manualmente los ensamblados necesarios al archivo link.xml puede no ser la forma más sencilla de evitar que se eliminen. Durante las revisiones de proyectos que realizo, como parte de mi trabajo como Consultor de Desarrollo de Software de Unity, he recibido preguntas de clientes sobre cómo pueden manejar mejor la eliminación de código administrado. Por eso he reunido estos consejos y mejores prácticas que pueden mejorar tu flujo de trabajo con el apoyo de los nuevos atributos de anotación de eliminación de código gestionado.

Eliminación de código gestionado con el enlazador Unity

La eliminación del código no utilizado es especialmente importante cuando se utiliza el backend de scripting IL2CPP. El enlazador Unity, una versión del enlazador Mono IL personalizada para trabajar con Unity, realiza un análisis estático para despojar el código gestionado.

Unity soporta tres niveles de eliminación de código gestionado para IL2CPP - Bajo, Medio y Alto. El manual de eliminación de código gestionado explica cómo funciona el proceso de eliminación de código, qué factores eliminan determinado código y en qué se diferencian los niveles de eliminación. Resumiendo: Cuanto mayor sea el nivel de eliminación de código, más se esforzará el enlazador por encontrar y eliminar el código no utilizado. Puede modificar el nivel de eliminación gestionado en la configuración del reproductor de su proyecto.

El análisis estático, aprovechado por el enlazador para la identificación de código no utilizado, no puede cubrir todos los casos en los que el tipo de un determinado objeto sólo se define en tiempo de ejecución. Estos casos dan lugar a resultados falsos positivos. Aunque puede que no haya ninguna referencia a una clase o método durante la compilación, la clase o el método siguen siendo necesarios para algunas partes del código en tiempo de ejecución. En este contexto, el código que utiliza Reflection sirve de buen ejemplo. Considere el siguiente fragmento:

Aunque se trata de un tipo de código válido y de uso común, el enlazador no sabe si MiAsamblea, MiTipo y MiMétodo se utilizan realmente en tiempo de ejecución. Esto puede hacer que se eliminen y, por extensión, provocar un error de ejecución. Consulte el manual de restricciones de desmontaje para obtener más información.

Los desarrolladores que utilizan frameworks de Inyección de Dependencia como Zenject, o librerías de serialización como Newtonsoft.Json, tienen que ser conscientes de que la eliminación de código falso-positivo es una posibilidad, y deben abordarla en consecuencia. He aquí algunos de los enfoques más comunes:

  • El enlazador reconoce una serie de atributos y permite anotar dependencias cuando no puede identificarlas. De este modo, puede añadir el atributo [Preserve] a los ensamblados, clases y métodos que no deban eliminarse.
  • Un archivo link.xml es una lista por proyecto que describe cómo preservar ensamblados, tipos y otras entidades de código dentro de ellos. Usted puede agregar manualmente los ensamblados, clases y métodos necesarios a link.xml, o utilizar la API de UnityEditor GenerateAdditionalLinkXmlFile para generar el archivo link.xml durante el proceso de construcción.

Incluso el paquete Addressables aprovecha el LinkXmlGenerator. El script de construcción de Addressables revisa la lista de activos en los grupos y añade los tipos utilizados por los activos en el archivo link.xml. También añade tipos utilizados internamente por Addressables mediante Reflection en tiempo de ejecución. Considere revisar el script de compilación predeterminado, BuildScriptPackedMode.cs,para obtener más detalles sobre la implementación de una solución similar como un paso en su proceso de compilación, como con el Scriptable Build Pipeline.

Unity soporta múltiples archivos link.xml localizados dentro de la carpeta Assets o una de sus subcarpetas. Durante el proceso de compilación, las entradas de varios archivos link.xml se fusionan y el enlazador las tiene en cuenta.

Novedades para la eliminación de código gestionado en Unity 2020 LTS

El uso del atributo [Conservar] puede requerir cierto trabajo manual. Pero si su proyecto ya está en Unity 2020 LTS, puede utilizar una serie de nuevos atributos de anotación de eliminación de código administrado para marcar fácilmente y con precisión ensamblados, clases y métodos que no deben eliminarse durante la eliminación de código. He aquí algunas de ellas:

  • RequireAttributeUsagesAttribute: Cuando se marca un tipo de atributo, todos los CustomAttributes de ese tipo también se marcarán, lo que reduce las complicaciones cuando se trabaja en el nivel de despojo Alto.
  • RequireDerivedAttribute: Cuando se marca un tipo, todos los tipos derivados de ese tipo se marcarán de forma similar.
  • RequiredInterfaceAttribute: Cuando se marca un tipo, se marcarán todas las implementaciones de interfaz de los tipos especificados.
  • RequiredMemberAttribute: Cuando se marca un tipo, se marcarán todos sus miembros con [RequiredMember]. Esto hace que la eliminación de código sea más precisa, ya que evitará que el tipo declarante se convierta en unstrippable. Tenga en cuenta, sin embargo, que si la propia clase no se utiliza, los miembros también serán eliminados, a pesar de estar marcados con el atributo [RequiredMember].
  • RequireImplementorsAttribute: Cuando se marca el tipo de interfaz, se marcarán todos los tipos que implementen esa interfaz. Por tanto, no es necesario marcar cada aplicación. Sin embargo, si la interfaz no está implementada en ninguna parte de la base de código, se eliminará a pesar de que esté marcada con el atributo [RequireImplementors].

En Unity 2020.1 y 2020.2, la herramienta recibió actualizaciones de la API para coincidir con el enlazador Mono IL. Ahora puede detectar algunos patrones de reflexión simples, lo que significa que si has actualizado a Unity 2020 LTS, tienes menos razones para usar archivos link.xml.

Para obtener más información sobre cómo Unity 2020 LTS puede ayudar a optimizar sus flujos de trabajo de codificación, consulte esta descripción general de características y la página de actualizaciones en el manual de Unity 2020 LTS.

De cara al futuro

Como parte de nuestro objetivo de 2021 de facilitarle la entrega de compilaciones de alta calidad a sus probadores y jugadores, nos hemos centrado en mejorar los flujos de trabajo de extracción de código. Más concretamente, en la versión 2021.2 hemos añadido un nuevo nivel de eliminación gestionado denominado Mínimo. Este será el valor por defecto para el backend IL2CPP, así que asegúrese de estar atento.

Nivel de extracción gestionado