* Pour autant que je sache, Unity3D pour iOS est basé sur le runtime Mono et Mono n'a que GC génération et balayage de génération.
Ce système GC ne peut pas éviter le temps GC qui arrête le système de jeu. Le regroupement d'instances peut réduire cela mais pas complètement, car nous ne pouvons pas contrôler l'instanciation qui se produit dans la bibliothèque de classes de base du CLR. Ces petites instances fréquentes et cachées augmenteront éventuellement le temps GC non déterministe. Forcer périodiquement un GC complet dégradera considérablement les performances (Mono peut-il forcer un GC complet, en fait?)
Alors, comment puis-je éviter ce temps GC lors de l'utilisation de Unity3D sans dégrader les performances?
unity
performance
Eonil
la source
la source
Réponses:
Heureusement, comme vous l'avez souligné, les versions COMPACT Mono utilisent un GC générationnel (contrairement à celles de Microsoft, comme WinMo / WinPhone / XBox, qui ne font que maintenir une liste plate).
Si votre jeu est simple, le GC devrait le gérer très bien, mais voici quelques conseils que vous voudrez peut-être examiner.
Optimisation prématurée
Assurez-vous d'abord que c'est réellement un problème avant d'essayer de le résoudre.
Mise en commun des types de référence coûteux
Vous devez regrouper les types de référence que vous créez souvent ou qui ont des structures profondes. Un exemple de chacun serait:
Bullet
objet dans un jeu d'enfer .Vous devez utiliser a
Stack
comme pool (contrairement à la plupart des implémentations qui utilisent aQueue
). La raison en est qu'avec unStack
si vous renvoyez un objet dans le pool et quelque chose d'autre le saisit immédiatement; il aura beaucoup plus de chances d'être dans une page active - ou même dans le cache CPU si vous êtes chanceux. C'est juste un tout petit peu plus rapide. De plus, limitez toujours la taille de vos pools (ignorez simplement les «enregistrements» si votre limite a été dépassée).Évitez de créer de nouvelles listes pour les effacer
Ne créez pas de nouveau
List
quand vous le vouliez réellementClear()
. Vous pouvez réutiliser le tableau principal et enregistrer une charge d'allocations et de copies de tableau. De la même manière, essayez de créer des listes avec une capacité initiale significative (rappelez-vous, ce n'est pas une limite - juste une capacité de départ) - cela n'a pas besoin d'être précis, juste une estimation. Cela devrait s'appliquer à pratiquement n'importe quel type de collection - à l'exception de aLinkedList
.Utilisez des tableaux (ou des listes) de structures dans la mesure du possible
Vous gagnez peu d'avantages à utiliser des structures (ou des types de valeurs en général) si vous les passez entre les objets. Par exemple, dans la plupart des «bons» systèmes de particules, les particules individuelles sont stockées dans un réseau massif: le réseau et l'indice sont transmis à la place de la particule elle-même. La raison pour laquelle cela fonctionne si bien est que lorsque le GC doit collecter le tableau, il peut ignorer le contenu entièrement (c'est un tableau primitif - rien à faire ici). Ainsi, au lieu de regarder 10 000 objets, le GC doit simplement regarder 1 tableau: un gain énorme! Encore une fois, cela ne fonctionnera qu'avec les types de valeur .
Après RoyT. a fourni des commentaires viables et constructifs, je pense que je dois développer davantage. Vous ne devez utiliser cette technique que lorsque vous avez affaire à des quantités massives d'entités (des milliers à des dizaines de milliers). De plus, ce doit être une structure qui ne doit pas avoir de champs de type référence et doit vivre dans un tableau explicitement typé. Contrairement à ses commentaires, nous le plaçons dans un tableau qui est très probablement un champ dans une classe - ce qui signifie qu'il va atterrir le tas (nous n'essayons pas d'éviter une allocation de tas - simplement en évitant le travail GC). Nous nous soucions vraiment du fait qu'il s'agit d'un bloc de mémoire contigu avec beaucoup de valeurs que le GC peut simplement regarder dans une
O(1)
opération au lieu d'uneO(n)
opération.Vous devez également allouer ces tableaux le plus près possible du démarrage de votre application pour atténuer les risques de fragmentation ou de travail excessif lorsque le GC essaie de déplacer ces morceaux (et envisagez d'utiliser une liste liée hybride au lieu du
List
type intégré ). ).GC.Collect ()
C'est définitivement LA MEILLEURE façon de vous tirer une balle dans le pied (voir: "Considérations de performances") avec un GC générationnel. Vous ne devriez l'appeler que lorsque vous avez créé une quantité EXTRÊME de déchets - et la seule instance où cela pourrait être un problème est juste après avoir chargé le contenu pour un niveau - et même alors, vous ne devriez probablement collecter que la première génération (
GC.Collect(0);
) avec un peu de chance pour empêcher la promotion d'objets à la troisième génération.IDisposable et Field Nulling
Il vaut la peine d'annuler les champs lorsque vous n'avez plus besoin d'un objet (plus encore sur les objets contraints). La raison en est dans les détails du fonctionnement du GC: il supprime uniquement les objets qui ne sont pas enracinés (c.-à-d. Référencés) même si cet objet aurait été non raciné en raison du retrait d'autres objets dans la collection actuelle ( remarque: cela dépend du GC saveur utilisée - certains nettoient les chaînes). De plus, si un objet survit à une collection, il est immédiatement promu à la génération suivante - cela signifie que tout objet laissé traîner dans les champs sera promu lors d'une collection. Chaque génération successive est exponentiellement plus coûteuse à collecter (et se produit aussi rarement).
Prenons l'exemple suivant:
S'il
MyFurtherNestedObject
contient un objet de plusieurs mégaoctets, vous pouvez être assuré que le GC ne le regardera pas assez longtemps - parce que vous l'avez par inadvertance promu en G3. Comparez cela avec cet exemple:Le modèle Disposer vous aide à configurer un moyen prévisible de demander aux objets d'effacer leurs champs privés. Par exemple:
la source
Très peu d'instanciation dynamique se produit automatiquement dans la bibliothèque de base, sauf si vous appelez quelque chose qui l'exige. La grande majorité de l'allocation et de la désallocation de mémoire provient de votre propre code et vous pouvez contrôler cela.
Si je comprends bien, vous ne pouvez pas l'éviter complètement - tout ce que vous pouvez faire est de vous assurer que vous recyclez et regroupez les objets lorsque cela est possible, utilisez des structures au lieu de classes, évitez le système UnityGUI et évitez généralement de créer de nouveaux objets dans la mémoire dynamique pendant les périodes quand la performance compte.
Vous pouvez également forcer la collecte des ordures à certains moments, ce qui peut ou non vous aider: voir certaines des suggestions ici: http://unity3d.com/support/documentation/Manual/iphone-Optimizing-Scripts.html
la source
GC.Collect()
= mémoire libre maintenant mais problèmes très probables plus tard.