
Consejos de perfilado de rendimiento para desarrolladores de juegos
Un rendimiento fluido es esencial para crear experiencias de juego inmersivas para los jugadores. Para asegurar que tu juego esté optimizado, un flujo de trabajo de perfilado consistente de principio a fin es un "imprescindible" para un desarrollo de juegos eficiente, y comienza con un simple procedimiento de tres puntos:
- Perfila antes de hacer cambios importantes: Establece una línea base.
- Perfilado durante el desarrollo: Rastrea y asegura que los cambios no rompan el rendimiento o los presupuestos.
- Perfila después: Demuestra que los cambios tuvieron el efecto deseado.
Esta página describe un flujo de trabajo de perfilado general para desarrolladores de juegos. Se extrae del libro electrónico, Guía definitiva para el perfilado de juegos de Unity, disponible para descargar gratis (la versión del guía de Unity 6 estará disponible pronto). El libro electrónico fue creado por expertos en desarrollo de juegos, perfilado y optimización, tanto externos como internos de Unity.
En este artículo puedes aprender sobre objetivos útiles que establecer con el perfilado, cuellos de botella de rendimiento comunes, como estar limitado por la CPU o la GPU, y cómo identificar e investigar estas situaciones con más detalle.

Establece un presupuesto de fotogramas
Los jugadores a menudo miden el rendimiento utilizando la tasa de fotogramas, o fotogramas por segundo (fps), pero como desarrollador, generalmente se recomienda usar tiempo de fotograma en milisegundos en su lugar. Considere el siguiente escenario simplificado:
Durante el tiempo de ejecución, su juego renderiza 59 fotogramas en 0.75 segundos. Sin embargo, el siguiente fotograma tarda 0.25 segundos en renderizarse. La tasa de fotogramas promedio entregada de 60 fps suena bien, pero en realidad los jugadores notarán un efecto de tartamudeo ya que el último fotograma tarda un cuarto de segundo en renderizarse.
Esta es una de las razones por las que es importante apuntar a un presupuesto de tiempo específico por fotograma. Esto le proporciona un objetivo sólido hacia el cual trabajar al perfilar y optimizar su juego, y en última instancia, crea una experiencia más fluida y consistente para sus jugadores.
Cada fotograma tendrá un presupuesto de tiempo basado en su fps objetivo. Una aplicación que apunte a 30 fps debería tomar siempre menos de 33.33 ms por fotograma (1000 ms / 30 fps). Del mismo modo, un objetivo de 60 fps deja 16.66 ms por fotograma (1000 ms / 60 fps).
Puede exceder este presupuesto durante secuencias no interactivas, por ejemplo, al mostrar menús de UI o cargar escenas, pero no durante el juego. Incluso un solo fotograma que exceda el presupuesto de fotograma objetivo causará tirones.
Nota: Una tasa de fotogramas consistentemente alta en juegos de VR es esencial para evitar causar náuseas o incomodidad a los jugadores, y a menudo es necesaria para que su juego obtenga la certificación del titular de la plataforma.
Fotogramas por segundo: Una métrica engañosa
Una forma común en que los jugadores miden el rendimiento es con la tasa de fotogramas, o fotogramas por segundo. Sin embargo, se recomienda que uses el tiempo de fotograma en milisegundos en su lugar. Para entender por qué, mira el gráfico anterior de fps versus tiempo de fotograma.
Considera estos números:
1000 ms/seg / 900 fps = 1.111 ms por fotograma
1000 ms/seg / 450 fps = 2.222 ms por fotograma
1000 ms/seg / 60 fps = 16.666 ms por fotograma
1000 ms/seg / 56.25 fps = 17.777 ms por fotograma
Si tu aplicación está funcionando a 900 fps, esto se traduce en un tiempo de fotograma de 1.111 milisegundos por fotograma. A 450 fps, esto es 2.222 milisegundos por fotograma. Esto representa una diferencia de solo 1.111 milisegundos por fotograma, aunque la tasa de fotogramas parece caer a la mitad.
Si miras las diferencias entre 60 fps y 56.25 fps, eso se traduce en 16.666 milisegundos por fotograma y 17.777 milisegundos por fotograma, respectivamente. Esto también representa 1.111 milisegundos extra por fotograma, pero aquí, la caída en la tasa de fotogramas se siente mucho menos dramática en términos porcentuales.
Por eso los desarrolladores utilizan el tiempo promedio de fotograma para medir la velocidad del juego en lugar de fps.
No te preocupes por los fps a menos que caigas por debajo de tu tasa de fotogramas objetivo. Concéntrate en el tiempo de fotograma para medir qué tan rápido está funcionando tu juego, luego mantente dentro de tu presupuesto de fotogramas.
Lee el artículo original, “Robert Dunlop’s fps versus frame time,” para más información.

Desafíos móviles
El control térmico es una de las áreas más importantes a optimizar al desarrollar aplicaciones para dispositivos móviles. Si la CPU o GPU pasan demasiado tiempo trabajando a toda potencia debido a un código ineficiente, esos chips se calentarán. Para evitar el sobrecalentamiento y el daño potencial a los chips, el sistema operativo reducirá la velocidad del reloj del dispositivo para permitir que se enfríe, lo que causará tartamudeo en los fotogramas y una mala experiencia de usuario. Esta reducción de rendimiento se conoce como estrangulación térmica.
Las tasas de fotogramas más altas y el aumento de la ejecución de código (o las operaciones de acceso a DRAM) conducen a un mayor consumo de batería y generación de calor. Un mal rendimiento también puede hacer que tu juego sea injugable para segmentos enteros de dispositivos móviles de gama baja, lo que puede llevar a oportunidades de mercado perdidas.
Al abordar el problema de la temperatura, considera el presupuesto con el que tienes que trabajar como un presupuesto a nivel de sistema.
Combate la estrangulación térmica y el drenaje de batería perfilando desde el principio para optimizar tu juego desde el inicio. Ajusta la configuración de tu proyecto para el hardware de la plataforma objetivo para combatir los problemas de temperatura y drenaje de batería.
Ajusta los presupuestos de fotogramas en móviles
Un consejo general para combatir los problemas térmicos del dispositivo durante tiempos de juego prolongados es dejar un tiempo de inactividad de fotogramas de alrededor del 35%. Esto le da a los chips móviles tiempo para enfriarse y ayuda a prevenir un drenaje excesivo de la batería. Usando un tiempo de fotograma objetivo de 33.33 ms por fotograma (para 30 fps), el presupuesto de fotogramas para dispositivos móviles será de aproximadamente 22 ms por fotograma.
El cálculo se ve así: (1000 ms / 30) * 0.65 = 21.66 ms
Para lograr 60 fps en móviles usando el mismo cálculo, se requeriría un tiempo de fotograma objetivo de (1000 ms / 60) * 0.65 = 10.83 ms. Esto es difícil de lograr en muchos dispositivos móviles y drenaría la batería el doble de rápido que apuntar a 30 fps. Por estas razones, muchos juegos móviles apuntan a 30 fps en lugar de 60. Usa Application.targetFrameRate para controlar esta configuración, y consulta la sección "Establecer un presupuesto de fotogramas" en el e-book de perfilado para más detalles sobre el tiempo de fotograma.
La escalabilidad de frecuencia en chips móviles puede dificultar la identificación de tus asignaciones de presupuesto de tiempo de inactividad de fotogramas al perfilar. Tus mejoras y optimizaciones pueden tener un efecto neto positivo, pero el dispositivo móvil podría estar reduciendo la frecuencia, y como resultado, funcionando más fresco. Usa herramientas personalizadas como FTrace o Perfetto para monitorear las frecuencias de los chips móviles, el tiempo de inactividad y la escalabilidad antes y después de las optimizaciones.
Siempre que te mantengas dentro de tu presupuesto total de tiempo de cuadro para tu fps objetivo (digamos 33.33 ms para 30 fps) y veas que tu dispositivo trabaja menos o registra temperaturas más bajas para mantener esta tasa de cuadros, entonces estás en el camino correcto.
Otra razón para agregar margen al presupuesto de cuadros en dispositivos móviles es tener en cuenta las fluctuaciones de temperatura en el mundo real. En un día caluroso, un dispositivo móvil se calentará y tendrá problemas para disipar el calor, lo que puede llevar a la reducción del rendimiento térmico y un mal rendimiento en los juegos. Reserva un porcentaje del presupuesto de cuadros para ayudar a evitar este escenario.

Reduce las operaciones de acceso a memoria
El acceso a la DRAM es típicamente una operación que consume mucha energía en dispositivos móviles. El consejo de optimización de Arm para contenido gráfico en dispositivos móviles dice que el acceso a la memoria LPDDR4 cuesta aproximadamente 100 picojulios por byte.
Reduce el número de operaciones de acceso a la memoria por cuadro mediante:
- Reducir la tasa de cuadros
- Reducir la resolución de la pantalla donde sea posible
- Usar mallas más simples con un recuento de vértices reducido y precisión de atributos
- Usar compresión de texturas y mipmapping
Cuando necesites enfocarte en dispositivos que aprovechan el hardware de CPU o GPU de Arm, la herramienta Arm Performance Studio (específicamente, Streamline Performance Analyzer) incluye algunos excelentes contadores de rendimiento para identificar problemas de ancho de banda de memoria. Los contadores disponibles están listados y explicados para cada generación de GPU de Arm en una guía de usuario correspondiente, por ejemplo, Guía de referencia de contadores de rendimiento de Mali-G710. Ten en cuenta que el perfilado de GPU de Arm Performance Studio requiere una GPU Arm Immortalis o Mali.
Establecer niveles de hardware para benchmarking
Además de usar herramientas de perfilado específicas de la plataforma, establece niveles o un dispositivo de especificaciones más bajas para cada plataforma y nivel de calidad que desees soportar, luego perfila y optimiza el rendimiento para cada una de estas especificaciones.
Como ejemplo, si estás apuntando a plataformas móviles, podrías decidir soportar tres niveles con controles de calidad que activan o desactivan características según el hardware objetivo. Luego optimizas para la especificación de dispositivo más baja en cada nivel. Como otro ejemplo, si estás desarrollando un juego para consolas, asegúrate de perfilar tanto en versiones más antiguas como en versiones más nuevas.
Nuestra última guía de optimización móvil tiene muchos consejos y trucos que te ayudarán a reducir la reducción térmica y aumentar la vida de la batería para dispositivos móviles que ejecutan tus juegos.
De perfilado de alto a bajo nivel
Al perfilar, deseas asegurarte de enfocar tu tiempo y esfuerzo en áreas donde puedas crear el mayor impacto. Por lo tanto, se recomienda comenzar con un enfoque de arriba hacia abajo al perfilar, lo que significa que comienzas con una visión general de alto nivel de categorías como renderizado, scripts, física y asignaciones de recolección de basura (GC). Una vez que hayas identificado áreas de preocupación, puedes profundizar en los detalles más profundos. Utiliza este paso de alto nivel para recopilar datos y tomar notas sobre los problemas de rendimiento más críticos, incluidos los escenarios que causan asignaciones gestionadas no deseadas o un uso excesivo de CPU en tu bucle de juego principal.
Primero necesitarás recopilar pilas de llamadas para los marcadores GC.Alloc. Si no estás familiarizado con este proceso, encuentra algunos consejos y trucos en la sección titulada "Localizando asignaciones de memoria recurrentes a lo largo de la vida de la aplicación" en el libro electrónico.
Si las pilas de llamadas reportadas no son lo suficientemente detalladas como para rastrear la fuente de las asignaciones u otras ralentizaciones, puedes realizar una segunda sesión de perfilado con el perfilado profundo habilitado para encontrar la fuente de las asignaciones. Cubrimos el perfilado profundo con más detalle en el libro electrónico, pero en resumen, es un modo en el Profiler que captura datos de rendimiento detallados para cada llamada de función, proporcionando información granular sobre los tiempos de ejecución y comportamientos, pero con un overhead significativamente mayor en comparación con el perfilado estándar.
Al recopilar notas sobre los "delincuentes" del tiempo de cuadro, asegúrate de anotar cómo se comparan en relación con el resto del cuadro. Este impacto relativo puede distorsionarse cuando se habilita el perfilado profundo, porque el perfilado profundo agrega un overhead significativo al instrumentar cada llamada de método.
Perfila temprano
Si bien siempre debes perfilar a lo largo de todo el ciclo de desarrollo de tu proyecto, las ganancias más significativas del perfilado se obtienen cuando comienzas en las fases tempranas.
Perfila temprano y a menudo para que tú y tu equipo comprendan y memoricen una "firma de rendimiento" para el proyecto que pueden usar como referencia. Si el rendimiento cae en picada, podrás identificar fácilmente cuándo las cosas salen mal y solucionar el problema.
Si bien el perfilado en el Editor te brinda una forma fácil de identificar los problemas principales, los resultados de perfilado más precisos siempre provienen de ejecutar y perfilar compilaciones en dispositivos de destino, junto con aprovechar herramientas específicas de la plataforma para profundizar en las características de hardware de cada plataforma. Esta combinación te proporcionará una visión holística del rendimiento de la aplicación en todos tus dispositivos de destino. Por ejemplo, podrías estar limitado por la GPU en algunos dispositivos móviles pero limitado por la CPU en otros, y solo puedes aprender esto midiendo en esos dispositivos.

Identifica problemas de rendimiento
Descarga la versión PDF imprimible de este gráfico aquí.
El objetivo del perfilado es identificar cuellos de botella como objetivos para la optimización. Si te basas en suposiciones, puedes terminar optimizando partes del juego que no son cuellos de botella, lo que resulta en poco o ningún mejora en el rendimiento general. Algunas "optimizaciones" incluso pueden empeorar el rendimiento general de tu juego, mientras que otras pueden ser laboriosas pero producir resultados insignificantes. La clave es optimizar el impacto de tu inversión de tiempo enfocada.
El diagrama de flujo anterior ilustra el proceso de perfilado inicial, con las secciones que lo siguen proporcionando información detallada sobre cada paso. También presentan capturas del Profiler de proyectos reales de Unity para ilustrar los tipos de cosas a buscar.
Para obtener una imagen holística de toda la actividad de la CPU, incluyendo cuando está esperando por la GPU, utiliza la vista de línea de tiempo en el módulo de CPU del Profiler. Familiarízate con los marcadores comunes del Profiler para interpretar las capturas correctamente. Algunos de los marcadores del Profiler pueden aparecer de manera diferente dependiendo de tu plataforma objetivo, así que dedica tiempo a explorar las capturas de tu juego en cada una de tus plataformas objetivo para tener una idea de cómo se ve una captura "normal" para tu proyecto.
El rendimiento de un proyecto está limitado por el chip y/o hilo que tarda más. Esa es el área en la que deben centrarse los esfuerzos de optimización. Por ejemplo, imagina los siguientes escenarios para un juego con un presupuesto de tiempo de fotograma objetivo de 33.33 ms y VSync habilitado:
- Si el tiempo de fotograma de la CPU (excluyendo VSync) es de 25 ms y el tiempo de la GPU es de 20 ms, ¡no hay problema! Estás limitado por la CPU, pero todo está dentro del presupuesto, y optimizar las cosas no mejorará la tasa de fotogramas (a menos que logres que tanto la CPU como la GPU estén por debajo de 16.66 ms y saltes a 60 fps).
- Si el tiempo de fotograma de la CPU es de 40 ms y la GPU es de 20 ms, estás limitado por la CPU y necesitarás optimizar el rendimiento de la CPU. Optimizar el rendimiento de la GPU no ayudará; de hecho, puede que quieras mover parte del trabajo de la CPU a la GPU, por ejemplo, utilizando shaders de cómputo en lugar de código C# donde sea aplicable, para equilibrar las cosas.
- Si el tiempo de fotograma de la CPU es de 20 ms y la GPU es de 40 ms, estás limitado por la GPU y necesitas optimizar el trabajo de la GPU.
- Si la CPU y la GPU están ambas a 40 ms, estás limitado por ambas y necesitarás optimizar ambas por debajo de 33.33 ms para alcanzar 30 fps.
Consulta estos recursos que exploran más sobre estar limitado por la CPU o la GPU:

¿Estás dentro del presupuesto de fotogramas?
Perfilar y optimizar tu proyecto temprano y a menudo durante el desarrollo te ayudará a asegurar que todos los hilos de CPU de tu aplicación y el tiempo total de fotogramas de GPU estén dentro del presupuesto de fotogramas. La pregunta que guiará este proceso es, ¿estás dentro del presupuesto de fotogramas o no?
Arriba hay una imagen de una captura de perfil de un juego móvil de Unity desarrollado por un equipo que realizó un perfilado y optimización continuos. El juego tiene como objetivo 60 fps en teléfonos móviles de alta especificación y 30 fps en teléfonos de especificación media/baja, como el que se muestra en esta captura.
Nota cómo casi la mitad del tiempo en el fotograma seleccionado está ocupada por el marcador de Profiler amarillo WaitForTargetFPS. La aplicación ha establecido Application.targetFrameRate en 30 fps, y VSync está habilitado. El trabajo de procesamiento real en el hilo principal termina alrededor de la marca de 19 ms, y el resto del tiempo se pasa esperando que transcurran los restantes 33.33 ms antes de comenzar el siguiente fotograma. Aunque este tiempo está representado con un marcador de Profiler, el hilo principal de la CPU está esencialmente inactivo durante este tiempo, permitiendo que la CPU se enfríe y use un mínimo de energía de la batería.
El marcador a tener en cuenta podría ser diferente en otras plataformas o si VSync está deshabilitado. Lo importante es verificar si el hilo principal está funcionando dentro de tu presupuesto de fotogramas o exactamente en tu presupuesto de fotogramas con algún tipo de marcador que indique que la aplicación está esperando VSync y si los otros hilos tienen algún tiempo inactivo.
El tiempo inactivo está representado por marcadores de Profiler grises o amarillos. La captura de pantalla anterior muestra que el hilo de renderizado está inactivo en Gfx.WaitForGfxCommandsFromMainThread, lo que indica momentos en los que ha terminado de enviar llamadas de dibujo a la GPU en un fotograma y está esperando más solicitudes de llamadas de dibujo de la CPU en el siguiente. De manera similar, aunque el hilo Job Worker 0 pasa algún tiempo en Canvas.GeometryJob, la mayor parte del tiempo está inactivo. Estos son todos signos de una aplicación que está cómodamente dentro del presupuesto de fotogramas.
Si tu juego está dentro del presupuesto de fotogramas
Si estás dentro del presupuesto de fotogramas, incluyendo cualquier ajuste realizado al presupuesto para tener en cuenta el uso de batería y la reducción térmica, has terminado con las tareas clave de perfilado. Puedes concluir ejecutando el Memory Profiler para asegurarte de que la aplicación también esté dentro de su presupuesto de memoria.
La imagen anterior muestra un juego funcionando cómodamente dentro del presupuesto de fotogramas de ~22 ms requerido para 30 fps. Nota el WaitForTargetfps que rellena el tiempo del hilo principal hasta VSync y los tiempos inactivos grises en el hilo de renderizado y el hilo de trabajo. También nota que el intervalo de VBlank se puede observar mirando los tiempos de finalización de Gfx.Present fotograma tras fotograma, y que puedes dibujar una escala de tiempo en el área de Línea de tiempo o en la regla de tiempo en la parte superior para medir de uno de estos al siguiente.

Limitado por la CPU
Si tu juego no está dentro del presupuesto de tiempo de la CPU, el siguiente paso es investigar qué parte de la CPU es el cuello de botella, en otras palabras, qué hilo está más ocupado.
Es raro que toda la carga de trabajo de la CPU sea el cuello de botella. Las CPUs modernas tienen varios núcleos diferentes, capaces de realizar trabajo de manera independiente y simultánea. Diferentes hilos pueden ejecutarse en cada núcleo de la CPU. Una aplicación completa de Unity utiliza una variedad de hilos para diferentes propósitos, pero aquellos que son más comunes para encontrar problemas de rendimiento son:
- El hilo principal: Aquí es donde la mayoría de la lógica/scripts del juego realizan su trabajo por defecto. La mayoría de los sistemas de Unity, como física, animación, UI y las etapas iniciales de renderizado, se ejecutan aquí.
- El hilo de renderizado: Esto maneja el trabajo de preparación (por ejemplo, qué objetos en la escena son visibles para la cámara y cuáles están excluidos/invisibles porque están fuera del frustum de vista, ocluidos o eliminados por otros criterios) que debe ocurrir antes de enviar instrucciones de renderizado a la GPU.
- Durante el proceso de renderizado, el hilo principal examina la escena y realiza la eliminación de cámaras, ordenación de profundidad y agrupamiento de llamadas de dibujo, resultando en una lista de cosas para renderizar. Esta lista se pasa al hilo de renderizado, que la traduce de la representación interna agnóstica de la plataforma de Unity a las llamadas específicas de la API gráfica requeridas para instruir a la GPU en una plataforma particular.
- Los hilos de trabajo de Job: Los desarrolladores pueden hacer uso del sistema de trabajos para programar ciertos tipos de trabajo que se ejecuten en hilos de trabajo, lo que reduce la carga de trabajo en el hilo principal. Algunos de los sistemas y características de Unity también hacen uso del sistema de trabajos, como física, animación y renderizado.
Un ejemplo del mundo real de optimización del hilo principal
La imagen a continuación muestra cómo podrían verse las cosas en un proyecto que está limitado por el hilo principal. Este proyecto se está ejecutando en un Meta Quest 2, que normalmente apunta a presupuestos de tiempo de 13.88 ms (72 fps) o incluso 8.33 ms (120 fps), porque altas tasas de fotogramas son importantes para evitar mareos en dispositivos de VR. Sin embargo, incluso si este juego estuviera apuntando a 30 fps, está claro que este proyecto tiene problemas.
Aunque el hilo de renderizado y los hilos de trabajo se ven similares al ejemplo que está dentro del presupuesto de fotogramas, el hilo principal está claramente ocupado con trabajo durante todo el fotograma. Incluso teniendo en cuenta la pequeña cantidad de sobrecarga del perfilador al final del fotograma, el hilo principal está ocupado durante más de 45 ms, lo que significa que este proyecto logra tasas de fotogramas de menos de 22 fps. No hay un marcador que muestre que el hilo principal está esperando inactivamente por VSync; está ocupado durante todo el fotograma.
La siguiente etapa de la investigación es identificar las partes del fotograma que tardan más tiempo y entender por qué es así. En este fotograma, PostLateUpdate.FinishFrameRendering toma 16.23 ms, más que el presupuesto total del fotograma. Una inspección más cercana revela que hay cinco instancias de un marcador llamado Inl_RenderCameraStack, indicando que cinco cámaras están activas y renderizando la escena. Dado que cada cámara en Unity invoca toda la tubería de renderizado, incluyendo culling, sorting y batching, la tarea de mayor prioridad para este proyecto es reducir el número de cámaras activas, idealmente a solo una.
BehaviourUpdate, el marcador del perfilador que abarca todas las ejecuciones del método MonoBehaviour.Update(), toma 7.27 milisegundos en este fotograma.
En la vista de Línea de tiempo, las secciones de color magenta indican puntos donde los scripts están asignando memoria de montón administrado. Al cambiar a la vista Hierarchy, y filtrar escribiendo GC.Alloc en la barra de búsqueda, se muestra que asignar esta memoria toma aproximadamente 0.33 ms en este fotograma. Sin embargo, esa es una medición inexacta del impacto que las asignaciones de memoria tienen en el rendimiento de su CPU.
Los marcadores GC.Alloc no se cronometra registrando un punto de inicio y fin como las muestras típicas del perfilador. Para minimizar su sobrecarga, Unity registra solo la marca de tiempo de la asignación y el tamaño asignado.
El perfilador asigna una pequeña duración de muestra artificial a los marcadores GC.Alloc únicamente para asegurarse de que sean visibles en las vistas del perfilador. La asignación real puede tardar más, especialmente si se necesita solicitar un nuevo rango de memoria del sistema. Para ver el impacto más claramente, coloque marcadores del perfilador alrededor del código que realiza la asignación; en el perfilado profundo, los espacios entre las muestras GC.Alloc de color magenta en la vista de Línea de tiempo proporcionan alguna indicación de cuánto tiempo podrían haber tomado.
Además, asignar nueva memoria puede tener efectos negativos en el rendimiento que son más difíciles de medir y atribuir directamente a ellos:
- Solicitar nueva memoria del sistema podría afectar el presupuesto de energía en un dispositivo móvil, lo que puede llevar al sistema a ralentizar la CPU o la GPU.
- La nueva memoria probablemente necesita ser cargada en la caché L1 de la CPU y, por lo tanto, empuja las líneas de caché existentes.
- La recolección de basura incremental o sincrónica puede ser activada directamente o con un retraso a medida que el espacio libre existente en la memoria administrada eventualmente se excede.
Al inicio del fotograma, cuatro instancias de Physics.FixedUpdate suman 4.57 ms. Más tarde, LateBehaviourUpdate (llamadas a MonoBehaviour.LateUpdate()) tardan 4 ms, y Animators representan aproximadamente 1 ms. Para asegurar que este proyecto cumpla con su presupuesto y tasa de fotogramas deseados, se deben investigar todos estos problemas del hilo principal para encontrar optimizaciones adecuadas.
Errores comunes para el cuello de botella del hilo principal
Las mayores ganancias de rendimiento se lograrán optimizando las cosas que tardan más tiempo. Las siguientes áreas son a menudo lugares fructíferos para buscar optimizaciones en proyectos que están limitados por el hilo principal:
- Cálculos de física
- Actualizaciones de scripts de MonoBehaviour
- Asignación y/o recolección de basura
- Culling y renderizado de cámara en el hilo principal
- Agrupamiento ineficiente de llamadas de dibujo
- Actualizaciones de UI, diseños y reconstrucciones
- Animación
Lee nuestras guías de optimización que ofrecen una larga lista de consejos prácticos para optimizar algunos de los errores comunes más frecuentes:
Dependiendo del problema que quieras investigar, otras herramientas también pueden ser útiles:
- Para scripts de MonoBehaviour que tardan mucho tiempo pero no te muestran exactamente por qué es así, añade Marcadores de Profiler al código o intenta profiling profundo para ver la pila de llamadas completa.
- Para scripts que asignan memoria gestionada, habilita Pilas de Llamadas de Asignación para ver exactamente de dónde provienen las asignaciones. Alternativamente, habilita el profiling profundo o usa Project Auditor, que muestra problemas de código filtrados por memoria, para que puedas identificar todas las líneas de código que resultan en asignaciones gestionadas.
- Usa el Depurador de Fotogramas para investigar las causas de un mal agrupamiento de llamadas de dibujo.
Para consejos completos sobre cómo optimizar tu juego, descarga estas guías de expertos de Unity de forma gratuita:

Limitado por CPU: Hilo de renderizado
Aquí hay un proyecto real que está limitado por su hilo de renderizado. Este es un juego de consola con una vista isométrica y un presupuesto de fotogramas objetivo de 33.33 ms.
La captura del Profiler muestra que antes de que se pueda comenzar a renderizar el fotograma actual, el hilo principal espera al hilo de renderizado, como indica el marcador Gfx.WaitForPresentOnGfxThread . El hilo de renderizado todavía está enviando comandos de llamada de dibujo del fotograma anterior y no está listo para aceptar nuevas llamadas de dibujo del hilo principal; también está gastando tiempo en Camera.Render.
Puedes notar la diferencia entre los marcadores relacionados con el fotograma actual y los marcadores de otros fotogramas, porque estos últimos aparecen más oscuros. También puedes ver que una vez que el hilo principal puede continuar y comenzar a emitir llamadas de dibujo para que el hilo de renderizado las procese, el hilo de renderizado tarda más de 100 ms en procesar el fotograma actual, lo que también crea un cuello de botella durante el siguiente fotograma.
Una investigación más profunda mostró que este juego tenía una configuración de renderizado compleja, que involucraba nueve cámaras diferentes y muchos pases adicionales causados por shaders de reemplazo. El juego también estaba renderizando más de 130 luces puntuales utilizando un camino de renderizado hacia adelante, lo que puede agregar múltiples llamadas de dibujo transparentes adicionales por cada luz. En total, estos problemas se combinaron para crear más de 3000 llamadas de dibujo por fotograma.
Trampas comunes para cuellos de botella en el hilo de renderizado
Las siguientes son causas comunes a investigar para proyectos que están limitados por el hilo de renderizado:
- Mal agrupamiento de llamadas de dibujo: Esto se aplica particularmente a las API gráficas más antiguas como OpenGL o DirectX 11.
- Demasiadas cámaras: A menos que estés haciendo un juego multijugador en pantalla dividida, lo más probable es que solo debas tener una Cámara activa.
- Mal culling: Esto resulta en demasiadas cosas siendo dibujadas. Investiga las dimensiones del frustum de tu Cámara y las máscaras de capa de culling.
El Módulo de Profiler de Renderizado muestra un resumen del número de lotes de llamadas de dibujo y llamadas de SetPass cada fotograma. La mejor herramienta para investigar qué lotes de llamadas de dibujo está emitiendo su hilo de renderizado a la GPU es el Depurador de Marcos.
Herramientas para resolver los cuellos de botella identificados
Si bien el enfoque de este libro electrónico es identificar problemas de rendimiento, las dos guías complementarias de optimización de rendimiento que destacamos anteriormente ofrecen sugerencias sobre cómo resolver los cuellos de botella, dependiendo de si su plataforma objetivo es PC o consola o móvil. En el contexto de los cuellos de botella del hilo de renderizado, vale la pena enfatizar que Unity ofrece diferentes sistemas y opciones de agrupamiento dependiendo de los problemas que haya identificado. Aquí hay un resumen rápido de algunas de las opciones que explicamos con mayor detalle en los libros electrónicos:
- El agrupamiento SRP reduce la sobrecarga de la CPU al almacenar datos de materiales de forma persistente en la memoria de la GPU. Si bien no reduce el número real de llamadas de dibujo, hace que cada llamada de dibujo sea más económica.
- La instanciación de GPU combina múltiples instancias de la misma malla utilizando el mismo material en una sola llamada de dibujo.
- El agrupamiento estático combina mallas estáticas (no móviles) que comparten el mismo material y, por lo tanto, puede ofrecerle ventajas al trabajar con un diseño de nivel con muchos elementos estáticos.
- El dibujador residente en GPU utiliza automáticamente la instanciación de GPU para reducir la sobrecarga de la CPU y las llamadas de dibujo, agrupando GameObjects similares.
- El agrupamiento dinámico combina mallas pequeñas en tiempo de ejecución, lo que puede ser una ventaja en dispositivos móviles más antiguos con altos costos de llamadas de dibujo. Sin embargo, la desventaja es que la transformación de vértices también puede ser intensiva en recursos.
- El recorte de oclusión de GPU utiliza sombreadores de cómputo para determinar la visibilidad de los objetos al comparar los búferes de profundidad de los fotogramas actuales y anteriores, reduciendo el renderizado innecesario de objetos ocluidos sin requerir datos prehorneados.
Además, en el lado de la CPU, se pueden utilizar técnicas como Camera.layerCullDistances para reducir el número de objetos enviados al hilo de renderizado al recortar objetos según su distancia de la cámara, ayudando a aliviar los cuellos de botella de la CPU durante el recorte de la cámara.
Estas son solo algunas de las opciones disponibles. Cada una de estas tiene diferentes ventajas y desventajas. Algunas están limitadas a ciertas plataformas. Los proyectos a menudo necesitan utilizar una combinación de varios de estos sistemas y, para hacerlo, es necesario entender cómo aprovechar al máximo cada uno de ellos.

Limitado por CPU: Hilos de trabajo
Los proyectos limitados por hilos de CPU que no sean el principal o el de renderizado no son tan comunes. Sin embargo, puede surgir si su proyecto utiliza el Data-Oriented Technology Stack (DOTS), especialmente si el trabajo se traslada del hilo principal a hilos de trabajo utilizando el sistema de trabajos.
La imagen anterior es una captura del modo de reproducción en el Editor, mostrando un proyecto DOTS ejecutando una simulación de fluido de partículas en la CPU.
Parece un éxito a primera vista. Los hilos de trabajo están llenos de trabajos Burst-compiled, lo que indica que se ha trasladado una gran cantidad de trabajo del hilo principal. Por lo general, esta es una decisión acertada.
Sin embargo, en este caso, el tiempo de cuadro de 48.14 ms y el marcador gris WaitForJobGroupID de 35.57 ms en el hilo principal son signos de que no todo está bien. WaitForJobGroupID indica que el hilo principal ha programado trabajos para ejecutarse de forma asíncrona en hilos de trabajo, pero necesita los resultados de esos trabajos antes de que los hilos de trabajo hayan terminado de ejecutarlos. Los marcadores azules del Profiler debajo de WaitForJobGroupID muestran al hilo principal ejecutando trabajos mientras espera, en un intento de asegurar que los trabajos terminen más pronto.
Aunque los trabajos están Burst-compiled, todavía están haciendo mucho trabajo. Quizás la estructura de consulta espacial utilizada por este proyecto para encontrar rápidamente qué partículas están cerca unas de otras debería ser optimizada o cambiada por una estructura más eficiente. O, los trabajos de consulta espacial pueden programarse para el final del cuadro en lugar de al principio, con los resultados no requeridos hasta el inicio del siguiente cuadro. Quizás este proyecto esté tratando de simular demasiadas partículas. Se requiere un análisis más detallado del código de los trabajos para encontrar la solución, por lo que agregar marcadores de Profiler más finos puede ayudar a identificar sus partes más lentas.
Los trabajos en tu proyecto pueden no estar tan paralelizados como en este ejemplo. Quizás solo tengas un trabajo largo ejecutándose en un solo hilo de trabajo. Esto está bien, siempre que el tiempo entre la programación del trabajo y el tiempo que necesita ser completado sea lo suficientemente largo para que el trabajo se ejecute. Si no es así, verás que el hilo principal se detiene mientras espera que el trabajo se complete, como en la captura de pantalla anterior.
Trampas comunes para cuellos de botella en hilos de trabajo
Las causas comunes de puntos de sincronización y cuellos de botella en hilos de trabajo incluyen:
- Trabajos que no están siendo compilados por el compilador Burst
- Trabajos de larga duración en un solo hilo de trabajo en lugar de ser paralelizados en múltiples hilos de trabajo
- Tiempo insuficiente entre el punto en el marco cuando se programa un trabajo y el punto cuando se requieren los resultados
- Múltiples "puntos de sincronización" en un marco, que requieren que todos los trabajos se completen de inmediato
Puedes usar la característica Eventos de Flujo en la vista de Línea de Tiempo del módulo de Perfilador de Uso de CPU para investigar cuándo se programan los trabajos y cuándo se esperan sus resultados por el hilo principal.
Para más información sobre cómo escribir código DOTS eficiente, consulta la guía Mejores Prácticas de DOTS.

Limitado por GPU
Tu aplicación está limitada por la GPU si el hilo principal pasa mucho tiempo en marcadores de Perfilador como Gfx.WaitForPresentOnGfxThread, y tu hilo de renderizado muestra simultáneamente marcadores como Gfx.PresentFrame o .WaitForLastPresent.
La mejor manera de obtener tiempos de cuadro de GPU es usar una herramienta de perfilado de GPU específica para la plataforma de destino, pero no todos los dispositivos facilitan la captura de datos confiables.
La API de FrameTimingManager puede ser útil en esos casos, proporcionando tiempos de cuadro de alto nivel y bajo costo tanto en la CPU como en la GPU.
La captura anterior se realizó en un teléfono móvil Android utilizando la API gráfica Vulkan. Aunque parte del tiempo pasado en Gfx.PresentFrame en este ejemplo podría estar relacionado con la espera de VSync, la longitud extrema de este marcador de Perfilador indica que la mayor parte de este tiempo se pasa esperando que la GPU termine de renderizar el cuadro anterior.
En este juego, ciertos eventos de juego activaron el uso de un shader que triplicó el número de llamadas de dibujo renderizadas por la GPU. Problemas comunes a investigar al perfilar el rendimiento de la GPU incluyen:
- Efectos de post-procesamiento de pantalla completa costosos, como Oclusión Ambiental y Bloom
- Shaders de fragmento costosos causados por:
- Lógica de ramificación dentro del código del shader
- Uso de precisión de punto flotante completa en lugar de precisión media, especialmente en dispositivos móviles
- Uso excesivo de registros, que afecta la ocupación de la ola de las GPUs
- Dibujo excesivo en la cola de renderizado Transparente causado por:
- Renderizado de UI ineficiente
- Uso superpuesto o excesivo de sistemas de partículas
- Efectos de post-procesamiento
- Resoluciones de pantalla excesivamente altas, como:
- pantallas 4K
- pantallas Retina en dispositivos móviles
- Micro triángulos causados por:
- geometría de malla densa
- Falta de sistemas de Nivel de Detalle (LOD), que es un problema particular en las GPU móviles, pero también puede afectar a las GPU de PC y consola
- Faltas de caché y ancho de banda de memoria GPU desperdiciado causados por:
- texturas sin comprimir
- texturas de alta resolución sin mipmaps
- sombras dinámicas, que pueden ejecutarse múltiples veces por cuadro si se habilitan los shaders de geometría o teselación
Si tu aplicación parece estar limitada por la GPU, puedes usar el Depurador de Cuadros como una forma rápida de entender los lotes de llamadas de dibujo que se envían a la GPU. Sin embargo, esta herramienta no puede presentar información específica sobre el tiempo de la GPU, solo cómo se construye la escena general.
La mejor manera de investigar la causa de los cuellos de botella de la GPU es examinar una captura de GPU de un perfilador de GPU adecuado. La herramienta que uses depende del hardware objetivo y de la API gráfica elegida. Consulta la sección de herramientas de perfilado y depuración en el libro electrónico para más información.

Encuentra más mejores prácticas y consejos de el centro de mejores prácticas de Unity. Elige entre más de 30 guías, creadas por expertos de la industria, ingenieros de Unity y artistas técnicos, que te ayudarán a desarrollar de manera eficiente con las herramientas y sistemas de Unity.