Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

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.

Screenshot du profileur de Godot

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.

Capture d'écran de Callgrind

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.

In this example, we can see nearly all time is spent under the Main::iteration() function. This is the master function in the Godot source code that is called repeatedly. It causes frames to be drawn, physics ticks to be simulated, and nodes and scripts to be updated. A large proportion of the time is spent in the functions to render a canvas (66%), because this example uses a 2D benchmark. Below this, we see that almost 50% of the time is spent outside Godot code in libglapi and i965_dri (the graphics driver). This tells us the a large proportion of CPU time is being spent in the graphics driver.

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 = OS.get_ticks_usec()

# Your function you want to time
update_enemies()

var time_end = OS.get_ticks_usec()
print("update_enemies() took %d microseconds" % time_end - time_start)

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 usually takes care of such low-level details for you. For example, the Server APIs make sure data is optimized for caching already for things like rendering and physics. Still, you should be especially aware of caching when writing 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 est conçu pour être facile à utiliser et à itérer, et est idéal pour réaliser de nombreux types de jeux. Toutefois, la facilité d'utilisation est considérée comme plus importante que la performance. Si vous devez faire des calculs lourds, pensez à déplacer une partie de votre projet vers l'un des autres langages.

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

Third parties provide support for several other languages, including Rust.

C++

Godot is written in C++. Using C++ will usually result in the fastest code. However, on a practical level, it is the most difficult to deploy to end users' machines on different platforms. Options for using C++ include GDExtensions and custom modules.

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

Although Nodes are an incredibly powerful and versatile concept, be aware that every node has a cost. Built-in functions such as _process() and _physics_process() propagate through the tree. This housekeeping can reduce performance when you have a very large numbers of nodes (how many exactly depends on the target platform and can range from thousands to tens of thousands so ensure that you profile performance on all target platforms during development).

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.

La solution au jitter est d'utiliser fixed timestep interpolation, qui consiste à lisser les positions et les rotations rendues sur plusieurs images pour correspondre à la physique. Vous pouvez soit l'implémenter vous-même, soit utiliser un addon tiers. Du point de vue des performances, l'interpolation est une opération très peu coûteuse par rapport à l'exécution d'une tique de physique. Elle est plus rapide d'un ordre de grandeur, ce qui peut être un gain de performance significatif tout en réduisant le jitter.