* Hasta donde sé, Unity3D para iOS se basa en el tiempo de ejecución de Mono y Mono solo tiene GC generacional de marca y barrido.
Este sistema GC no puede evitar el tiempo GC que detiene el sistema del juego. La agrupación de instancias puede reducir esto, pero no por completo, porque no podemos controlar la instanciación que ocurre en la biblioteca de clases base del CLR. Esas instancias pequeñas y frecuentes ocultas eventualmente aumentarán el tiempo de GC no determinista. Forzar un GC completo periódicamente degradará enormemente el rendimiento (¿puede Mono forzar un GC completo, en realidad?
Entonces, ¿cómo puedo evitar este tiempo de GC cuando uso Unity3D sin una gran degradación del rendimiento?
unity
performance
Eonil
fuente
fuente
Respuestas:
Afortunadamente, como usted señaló, el COMPACTO compilaciones Mono usan un GC generacional (en marcado contraste con los de Microsoft, como WinMo / WinPhone / XBox, que solo mantienen una lista plana).
Si su juego es simple, el GC debería manejarlo bien, pero aquí hay algunos consejos que puede considerar.
Optimización prematura
Primero asegúrese de que esto sea realmente un problema para usted antes de intentar solucionarlo.
Agrupación de tipos de referencia caros
Debe agrupar los tipos de referencia que crea con frecuencia o que tienen estructuras profundas. Un ejemplo de cada uno sería:
Bullet
objeto en un juego de infierno de balas .Debe usar a
Stack
como su grupo (a diferencia de la mayoría de las implementaciones que usan aQueue
). La razón de esto es porque con unStack
si devuelve un objeto al grupo y algo más lo agarra inmediatamente; Tendrá muchas más posibilidades de estar en una página activa, o incluso en el caché de la CPU si tiene suerte. Es un poquito más rápido. Además, siempre limite el tamaño de sus piscinas (solo ignore los 'registros' si se ha excedido su límite).Evite crear nuevas listas para borrarlas
No cree uno nuevo
List
cuando realmente lo haya queridoClear()
. Puede reutilizar la matriz de back-end y guardar una gran cantidad de asignaciones y copias de la matriz. De manera similar a este, intente y cree listas con una capacidad inicial significativa (recuerde, esto no es un límite, solo una capacidad inicial), no necesita ser precisa, solo una estimación. Esto debería aplicarse básicamente a cualquier tipo de colección, excepto aLinkedList
.Utilice matrices de estructura (o listas) donde sea posible
Obtiene pocos beneficios del uso de estructuras (o tipos de valor en general) si los pasa entre objetos. Por ejemplo, en la mayoría de los sistemas de partículas "buenas", las partículas individuales se almacenan en una matriz masiva: la matriz y el índice se pasan en lugar de la partícula misma. La razón por la que esto funciona tan bien es porque cuando el GC necesita recopilar la matriz, puede omitir el contenido por completo (es una matriz primitiva, no hay nada que hacer aquí). Entonces, en lugar de mirar 10 000 objetos, el GC simplemente necesita mirar 1 matriz: ¡gran ganancia! Nuevamente, esto solo funcionará con tipos de valor .
Después de RoyT. proporcionó algunos comentarios viables y constructivos. Siento que necesito ampliar más esto. Solo debe usar esta técnica cuando se trata de grandes cantidades de entidades (de miles a decenas de miles). Además, debe ser una estructura que no debe tener ningún campo de tipo de referencia y debe vivir en una matriz tipada explícitamente. Contrariamente a sus comentarios, lo estamos colocando en una matriz que es muy probable que sea un campo en una clase, lo que significa que va a aterrizar el montón (no estamos tratando de evitar una asignación de montón, simplemente evitando el trabajo de GC). Lo que realmente nos importa es el hecho de que es una porción contigua de memoria con muchos valores que el GC simplemente puede ver en una
O(1)
operación en lugar de unaO(n)
operación.También debe asignar estos arreglos lo más cerca posible al inicio de su aplicación para mitigar las posibilidades de que ocurra fragmentación o trabajo excesivo a medida que el GC intenta mover estos fragmentos (y considere usar una lista enlazada híbrida en lugar del
List
tipo incorporado) )GC.Collect ()
Esta es definitivamente LA MEJOR forma de dispararse en el pie (ver: "Consideraciones de rendimiento") con un GC generacional. Solo debe llamarlo cuando haya creado una cantidad EXTREMA de basura, y la única instancia en la que eso podría ser un problema es justo después de haber cargado el contenido para un nivel, e incluso entonces probablemente solo deba recolectar la primera generación (
GC.Collect(0);
) con suerte para evitar la promoción de objetos a la tercera generación.IDisposable y anulación de campo
Vale la pena anular los campos cuando ya no necesita un objeto (más aún en objetos restringidos). La razón está en los detalles de cómo funciona el GC: solo elimina objetos que no están enraizados (es decir, referenciados) incluso si ese objeto se hubiera desrooteado debido a que otros objetos se eliminaron en la colección actual ( nota: esto depende del GC sabor en uso - algunos realmente limpian las cadenas). Además, si un objeto sobrevive a una colección, se promociona inmediatamente a la próxima generación; esto significa que cualquier objeto que quede en los campos se promocionará durante una colección. Cada generación sucesiva es exponencialmente más costosa de recolectar (y ocurre con poca frecuencia).
Tome el siguiente ejemplo:
Si
MyFurtherNestedObject
contiene un objeto de varios megabytes, puede estar seguro de que el GC no lo mirará durante mucho tiempo, porque lo ha promovido inadvertidamente a G3. Contrasta eso con este ejemplo:El patrón Disposer te ayuda a configurar una forma predecible de pedirle a los objetos que borren sus campos privados. Por ejemplo:
fuente
Muy poca instanciación dinámica ocurre automáticamente dentro de la biblioteca base, a menos que llame a algo que lo requiera. La gran mayoría de la asignación de memoria y desasignación proviene de su propio código y puede controlarlo.
Según tengo entendido, no puede evitar esto por completo: todo lo que puede hacer es asegurarse de reciclar y agrupar objetos donde sea posible, usar estructuras en lugar de clases, evitar el sistema UnityGUI y, en general, evitar crear nuevos objetos en la memoria dinámica durante los tiempos cuando el rendimiento importa.
También puede forzar la recolección de basura en ciertos momentos, lo que puede o no ayudar: consulte algunas de las sugerencias aquí: http://unity3d.com/support/documentation/Manual/iphone-Optimizing-Scripts.html
fuente
GC.Collect()
= memoria libre ahora pero muy probablemente problemas después.