Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Optimisation CPU
Mesure des performances
We have to know where the "bottlenecks" are to know how to speed up our program. Bottlenecks are the slowest parts of the program that limit the rate that everything can progress. Focusing on bottlenecks allows us to concentrate our efforts on optimizing the areas which will give us the greatest speed improvement, instead of spending a lot of time optimizing functions that will lead to small performance improvements.
Pour le CPU, le moyen le plus simple d'identifier les goulets d'étranglement est d'utiliser un profileur.
CPU profileurs
Les profileurs fonctionnent en parallèle de votre programme et prennent des mesures de temps pour déterminer la proportion de temps passé dans chaque fonction.
L'IDE Godot dispose d'un profileur intégré. Il ne fonctionne pas à chaque fois que vous démarrez votre projet : il doit être démarré et arrêté manuellement. En effet, comme pour la plupart des profileurs, l'enregistrement de ces mesures de temps peut ralentir considérablement votre projet.
Après le profilage, vous pouvez consulter les résultats pour une image.
Résultats d'un profilage d'un des projets de démonstration.
Note
Nous pouvons voir le coût des processus intégrés tels que la physique et l'audio, ainsi que le coût de nos propres fonctions de script en bas.
Le temps passé à attendre les différents serveurs intégrés peut ne pas être comptabilisé dans les profileurs. Il s'agit d'un bug connu.
Lorsqu'un projet se déroule lentement, vous verrez souvent une fonction ou un processus évident prendre beaucoup plus de temps que d'autres. C'est votre principal goulot d'étranglement, et vous pouvez généralement augmenter la vitesse en optimisant cette partie.
Pour plus d'informations sur l'utilisation du profileur intégré de Godot, voir Panneau de débogage.
Profileurs externes
Bien que le profileur de l'IDE Godot soit très pratique et utile, il faut parfois plus de puissance et la capacité de profiler le code source du moteur Godot lui-même.
You can use a number of third-party C++ profilers to do this.
Exemples de résultats de Callgrind, qui fait partie de Valgrind.
De gauche à droite, Callgrind indique le pourcentage de temps passé dans une fonction et ses enfants (Inclusive), le pourcentage de temps passé dans la fonction elle-même, à l'exclusion des fonctions enfants (Self), le nombre de fois que la fonction est appelée, le nom de la fonction et le fichier ou module.
Dans cet exemple, nous pouvons voir que presque tout le temps est passé dans la fonction Main::iteration(). C'est la fonction maître dans le code source de Godot qui est appelée de façon répétée. Elle provoque le dessin des images, la simulation des tics physiques, et la mise à jour de nœuds et de scripts. Une grande partie du temps est consacrée aux fonctions de rendu d'un canevas (66%), car cet exemple utilise un benchmark 2D. En dessous, nous voyons que presque 50% du temps est passé en dehors du code de Godot dans libglapi et i965_dri (le pilote graphique). Cela nous indique qu'une grande partie du temps CPU est passé dans le pilote graphique.
C'est en fait un excellent exemple car dans un monde idéal, seule une très petite partie du temps serait consacrée au pilote graphique. Cela indique qu'il y a un problème avec trop de communication et de trop travail fait dans l'API graphique. Ce profilage a conduit au développement du traitement par lot 2D, qui accélère considérablement le rendu 2D en réduisant les goulots d'étranglement dans ce domaine.
Chronométrer manuellement des fonctions
Une autre technique pratique, surtout lorsque vous avez identifié le goulot d'étranglement à l'aide d'un profileur, consiste à chronométrer manuellement la fonction ou la zone testée. Les spécificités varient selon le langage, mais en GDScript, vous feriez ce qui suit :
var time_start = Time.get_ticks_usec()
# Your function you want to time
update_enemies()
var time_end = Time.get_ticks_usec()
print("update_enemies() took %d microseconds" % (time_end - time_start))
var timeStart = Time.GetTicksUsec();
// Your function you want to time.
UpdateEnemies();
var timeEnd = Time.GetTicksUsec();
GD.Print($"UpdateEnemies() took {timeEnd - timeStart} microseconds");
Lorsque vous chronométrez manuellement des fonctions, il est généralement judicieux d'exécuter la fonction plusieurs fois (1000 fois ou plus), au lieu d'une seule fois (à moins qu'il ne s'agisse d'une fonction très lente). Cela s'explique en grande partie par le fait que les timers ont souvent une précision limitée, et que les CPU organisent les processus de manière aléatoire. Par conséquent, une moyenne sur une série d'exécutions est plus précise qu'une mesure unique.
Lorsque vous essayez d'optimiser les fonctions, veillez à les profiler ou à les chronométrer au fur et à mesure. Cela vous permettra d'obtenir un retour d'information crucial pour savoir si l'optimisation fonctionne (ou non).
Caches
Les caches duCPU sont une autre chose à laquelle il faut être particulièrement attentif, surtout lorsqu'on compare les résultats de chronométrage de deux versions différentes d'une fonction. Les résultats peuvent être très dépendants du fait que les données se trouvent ou non dans le cache du CPU. Les CPU ne chargent pas les données directement à partir de la RAM du système, même si celle-ci est énorme par rapport au cache du CPU (plusieurs gigaoctets au lieu de quelques mégaoctets). Cela s'explique par le fait que la mémoire vive du système est très lente en accès. Au lieu de cela, les CPU chargent des données à partir d'une banque de mémoire plus petite et plus rapide appelée cache. Le chargement de données à partir de la mémoire cache est très rapide, mais chaque fois que vous essayez de charger une adresse mémoire qui n'est pas stockée dans la mémoire cache, la mémoire cache doit faire un voyage vers la mémoire principale et charger lentement certaines données. Ce retard peut faire que le CPU reste inactif pendant un long moment, ce que l'on appelle un "cache miss".
Cela signifie que la première fois que vous exécutez une fonction, elle peut être lente, car les données ne sont pas en mémoire cache. La deuxième fois et les suivantes, elle peut s'exécuter beaucoup plus rapidement parce que les données sont en mémoire cache. Il faut donc toujours utiliser des moyennes lors du chronométrage, et être conscient des effets de cache.
La compréhension de la mise en cache est également cruciale pour l'optimisation CPU. Si vous disposez d'un algorithme (routine) qui charge de petits morceaux de données à partir de zones de la mémoire principale réparties de manière aléatoire, cela peut entraîner de nombreux cache misses, la plupart du temps, le CPU attendra des données au lieu d'effectuer un travail quelconque. Au lieu de cela, si vous pouvez faire en sorte que vos accès aux données soient localisés, ou mieux encore, si vous accédez à la mémoire de manière linéaire (comme une liste continue), alors le cache fonctionnera de manière optimale et le CPU pourra travailler aussi vite que possible.
Godot s'occupe généralement de ces détails de bas niveau pour vous. Par exemple, les API du serveur s'assurent que les données sont déjà optimisées pour la mise en cache pour des choses comme le rendu et la physique. Mais vous devez être particulièrement attentif à la mise en cache lorsque vous écrivez des GDExtensions.
Langages
Godot prend en charge un certain nombre de langues différentes, et il convient de garder à l'esprit qu'il y a des compromis à faire. Certains langages sont conçues pour être faciles à utiliser, au prix de la rapidité, et d'autres sont plus rapides mais plus difficiles à utiliser.
Les fonctions intégrées du moteur fonctionnent à la même vitesse, quel que soit le langage de script que vous choisissez. Si votre projet effectue beaucoup de calculs dans son propre code, envisagez de déplacer ces calculs vers un langage plus rapide.
GDScript
GDScript is designed to be easy to use and iterate, and is ideal for making many types of games. However, in this language, ease of use is considered more important than performance. If you need to make heavy calculations, consider moving some of your project to one of the other languages.
C#
C# is popular and has first-class support in Godot. It offers a good compromise between speed and ease of use. Beware of possible garbage collection pauses and leaks that can occur during gameplay, though. A common approach to workaround issues with garbage collection is to use object pooling, which is outside the scope of this guide.
Autres langages
Des tiers fournissent un support pour plusieurs autres langages, notamment Rust.
C++
Godot est écrit en C++. L'utilisation du C++ permet généralement d'obtenir le code le plus rapide. Cependant, d'un point de vue pratique, il est le plus difficile à déployer sur les machines des utilisateurs finaux sur différentes plateformes. Les options pour utiliser le C++ comprennent les GDExtensions et les modules personnalisés.
Tâches Parallèles
Pensez à utiliser des threads lorsque vous effectuez de nombreux calculs qui peuvent être parallèles les uns aux autres. Les CPU modernes ont plusieurs cœurs, chacun capable d'effectuer une quantité de travail limitée. En répartissant le travail sur plusieurs threads, vous pouvez aller plus loin vers une efficacité maximale du CPU.
The disadvantage of threads is that you have to be incredibly careful. As each CPU core operates independently, they can end up trying to access the same memory at the same time. One thread can be reading to a variable while another is writing: this is called a race condition. Before you use threads, make sure you understand the dangers and how to try and prevent these race conditions. Threads can make debugging considerably more difficult.
Pour plus d'informations sur les threads, voir Utiliser plusieurs fils d'exécution.
ArbreDesScènes
Bien que les Nodes soient un concept incroyablement puissant et polyvalent, sachez que chaque nœud a un coût. Des fonctions intégrées telles que _process() et _physics_process() se propagent dans l'arbre. Cette gestion interne peut réduire les performances lorsque vous avez un très grand nombre de nœuds (combien exactement dépend de la plateforme cible et peut aller de milliers à des dizaines de milliers, donc assurez vous d'inspecter la performance sur toutes les plateformes cibles durant le développement).
Chaque nœud est traité individuellement dans le moteur de rendu Godot. Par conséquent, un nombre plus petit de nœuds avec plus dans chacun peut conduire à une meilleure performance.
L'une des bizarreries de SceneTree est que vous pouvez parfois obtenir de bien meilleures performances en enlevant des nœuds du SceneTree, plutôt qu'en les mettant en pause ou en les cachant. Vous n'avez pas besoin de supprimer un nœud détaché. Vous pouvez par exemple conserver une référence à un nœud, le détacher de l'arbre des scènes avec Node.remove_child(node), puis le rattacher plus tard avec Node.add_child(node). Cela peut être très utile pour ajouter et supprimer des zones d'un jeu par exemple.
Vous pouvez éviter complètement SceneTree en utilisant les API serveur. Pour plus d'informations, voir Optimisation à l'aide de serveurs.
Physique
Dans certaines situations, la physique peut finir par devenir un goulot d'étranglement. C'est particulièrement le cas avec des mondes complexes et un grand nombre d'objets physiques.
Quelques techniques pour accélérer la physique :
Essayez d'utiliser des versions simplifiées de votre géométrie rendue pour la physique. Souvent, les utilisateurs finaux ne s'en apercevront pas, mais cela peut améliorer considérablement les performances.
Essayez de retirer des objets de la physique lorsqu'ils sont hors de vue / en dehors de la zone actuelle, ou de réutiliser des objets de la physique (peut-être que vous autorisez 8 monstres par zone, par exemple, et que vous les réutilisez).
Un autre aspect crucial de la physique est le taux de taux de rafraîchissement de la physique. Dans certains jeux, vous pouvez réduire considérablement le taux de taux de rafraîchissement, et au lieu, par exemple, de mettre à jour la physique 60 fois par seconde, vous pouvez la mettre à jour à 30, voire 20 fois par seconde. Cela peut réduire considérablement la charge du CPU.
L'inconvénient de la modification du taux de rafraîchissement de la physique est que vous pouvez obtenir un mouvement saccadé ou du jitter lorsque le taux de rafraîchissement de la physique ne correspond pas à celui du rendu des images. De plus, la diminution du taux de rafraîchissement des données physiques augmente le délai d'entrée. Il est recommandé de s'en tenir à la fréquence de rafraîchissement par défaut (60 Hz) dans la plupart des jeux qui présentent des mouvements du joueur en temps réel.
The solution to jitter is to use fixed timestep interpolation, which involves smoothing the rendered positions and rotations over multiple frames to match the physics. Godot has built-in physics interpolation which you can read about here. Performance-wise, interpolation is a very cheap operation compared to running a physics tick. It's orders of magnitude faster, so this can be a significant performance win while also reducing jitter.