- Messages
- 6 722
- Réactions
- 2 423
de JustSid , xp dev
... Comme d'autres l'ont mentionné de manière plus ou moins agréable, le problème ici est que votre CPU a plusieurs cœurs et Windows les additionne tous jusqu'à 100% du système. Ainsi, par exemple, si vous avez un système à 4 cœurs et qu'un seul est utilisé, Windows indiquera que l'utilisation du CPU est de 25%. D'autres systèmes d'exploitation rapportent l'utilisation du CPU différemment, par exemple macOS traitera chaque cœur séparément. Ainsi, sur une machine à 4 cœurs, l'utilisation du CPU peut atteindre 400 % et l'utilisation d'un seul cœur affichera une utilisation de 100 %.
Comme mentionné, la meilleure façon de savoir quelle partie du système est la plus sollicitée est de regarder les données de synchronisation fournies dans X-Plane. Vous obtenez un nombre pour le temps CPU et GPU, exprimé en secondes où la fraction est les millisecondes. Dans la capture d'écran ci-dessus, le temps CPU est de 0,0304 et le temps GPU est de 0,0250. Cela signifie que pour une seule image, X-Plane prend 30,4 millisecondes sur le CPU et 25 millisecondes sur le GPU. A partir de là, la conclusion est que le goulot d'étranglement est le CPU : Le GPU peut produire une image toutes les 25 millisecondes alors que le CPU peut en produire une toutes les 30 millisecondes. Pour augmenter le nombre d'images par seconde, il faut donc décharger un peu le CPU pour réduire le temps qu'il met. Comme point de référence, vous pouvez faire 1 / temps pour obtenir les FPS (parce qu'il mesure les images par _une_ seconde, donc c'est l'inverse du temps pour une seule image. Alternativement, 1 / FPS vous donne le temps maximum qu'une seule image peut prendre pour atteindre ce FPS). Dans votre cas, le GPU pourrait fonctionner à 1 / 0,025 = 40 FPS. Si vous visez 60 FPS, alors vous voudriez 1 / 60 = 0,016 comme objectif de temps de trame, ou 16 millisecondes maximum par trame.
En fin de compte, vous avez absolument raison, X-Plane laisse des ressources CPU sur la table dans ce cas.
Alors, laissez-moi prendre la tangente et expliquer pourquoi X-Plane n'utilise pas tous les cœurs du CPU à tout moment :
De manière inhérente, tous les programmes tournent par défaut sur un cœur de CPU, toutes les instructions s'exécutent linéairement (ne me faites pas le coup de l'ackshually sur les architectures super scalaires hors d'usage). Et pour tirer parti d'un autre cœur, le programme doit être écrit dans cet esprit, puis bifurquer de son exécution et faire tourner un autre thread sur l'autre cœur de CPU qui lui-même exécute aussi des instructions linéairement, mais probablement des instructions différentes. C'est là le cœur du problème : le programme doit avoir suffisamment de travail pour pouvoir être séparé et exécuté en parallèle avec le reste des autres cœurs. Il est difficile d'y parvenir, car il faut ajouter un certain nombre d'éléments supplémentaires au code : Par exemple, puisque les threads fonctionnent en parallèle sans garantie d'ordre, si vous avez besoin d'accéder à des données qui sont en train d'être écrites par un autre thread, vous devrez commencer à synchroniser les deux threads, ce qui signifie potentiellement qu'un thread doit attendre l'autre, gaspillant des cycles de CPU en ne faisant rien. Il est en fait assez facile de prendre naïvement un programme à un seul thread et d'écrire un code multithread qui est moins performant à cause de cela. Il n'est pas non plus très facile d'écrire du code multithread pour commencer, il n'y a pas de véritables sauvegardes et, comme il n'y a pas de garanties d'ordre, les bogues peuvent être extrêmement insidieux : une course aux données où deux threads ne sont pas correctement synchronisés peut fonctionner 99% du temps, puis, une fois le code envoyé aux clients, il commence à s'effondrer et à se planter de manière mystérieuse sans que l'on sache vraiment pourquoi. Gardez à l'esprit qu'il est facile d'écrire "vous devez synchroniser vos threads correctement" sur le papier, mais que c'est une toute autre affaire lorsque vous avez un programme de plusieurs millions de lignes de code où il devient beaucoup plus difficile de raisonner sur le code qui s'exécute dans quel contexte et dans quelles conditions. Cela s'aggrave lorsque vous avez une base de code qui, à l'origine, n'a pas été écrite pour être multithreadée pour des raisons historiques et qui doit ensuite être adaptée pour ce travail.
Il y a encore un autre problème ici. Outre la barrière à l'entrée pour écrire du code multithread, le problème suivant est que vous devez décomposer votre code en morceaux qui peuvent s'exécuter indépendamment. Certains problèmes sont gênants du point de vue du parallélisme, qui est le nom réel d'une catégorie de code ou d'algorithmes qui s'adaptent extrêmement bien à plusieurs threads. C'est le cas, par exemple, du traitement parallèle des données, où chaque thread peut lire un sous-ensemble de données et le traiter indépendamment. D'autres choses ne se prêtent pas aussi bien au multithreading, par exemple le rendu : Les commandes de rendu sont encodées dans un tampon de commande pour être exécutées par le GPU. Cet encodage doit être effectué dans l'ordre. Vous ne pouvez donc pas vous contenter d'un traitement parallèle, car vous devrez très rapidement remettre les résultats dans l'ordre, ce qui est extrêmement coûteux et lourd en termes de synchronisation. Pour X-Plane par exemple, nous avons la mise à jour de la physique : la mise à jour de la physique doit être exécutée en premier pour que nous puissions résoudre la position de la caméra, ensuite nous pouvons commencer le rendu car nous savons où nous sommes dans le monde. D'autre part, le moteur physique peut absolument calculer le modèle de vol de plusieurs avions en parallèle, car ils sont indépendants les uns des autres.
Les moteurs de jeu de la vieille école ont résolu ce problème en divisant le code en sous-catégories. Ainsi, par exemple, un thread s'occupait du rendu, un autre de la physique, un autre de l'audio et ainsi de suite. Le problème est que vous allez très vite manquer de catégories de haut niveau que vous pouvez décomposer et dans un monde de 32 cœurs ou plus, cette stratégie tombe à plat. Sans compter que si vous concevez un moteur pour 32 cœurs de cette manière, les utilisateurs disposant de moins de cœurs auront beaucoup de mal. L'approche moderne de ce problème est d'avoir un planificateur de tâches et d'exécuter un tas de petites tâches en parallèle, puis de synchroniser le travail aussi rarement que possible. Dans un tel système, vous pourriez avoir des mises à jour d'objets qui sont toutes lancées comme leurs propres tâches pour être traitées en parallèle, puis synchronisées pour être rendues correctement. Cette approche s'adapte très bien à de nombreux cœurs de processeur, car le planificateur de tâches n'a qu'à créer plus de threads et à répartir les tâches entre eux. Tant qu'il y a des tâches disponibles pour occuper tous les cœurs du processeur, vous êtes gagnant.
C'est exactement ce que fait aussi X-Plane. Cependant, la charge de travail qui est jobifiée est limitée dans X-Plane. En général, le chargement en arrière-plan est ce que X-Plane fait en parallèle. Ainsi, la pagination des nouvelles ressources artistiques et des données de terrain est effectuée en arrière-plan. C'est assez facile à synchroniser car tout ce que vous avez à faire est de ne pas utiliser les ressources artistiques avant qu'elles ne soient prêtes, il n'y a pas de consommateurs partagés de quelque chose qui vient d'être créé. Cependant, beaucoup de choses qui se passent d'une image à l'autre ne sont pas encore parallèles. Cela est principalement dû à des décisions de conception héritées qui ont été intégrées dans le moteur d'une manière ou d'une autre. Tout ce bazar doit être nettoyé avant que nous puissions commencer à paralléliser la trame. Ce travail figure en tête de notre liste de priorités, car rendre X-Plane plus rapide est un objectif assez important, mais c'est un travail facile et le bénéfice n'est pas là tant que tout n'est pas terminé. L'un de nos premiers objectifs est d'exécuter le rendu des ombres et le rendu de l'image principale en parallèle. En théorie, ils sont indépendants l'un de l'autre car ils ont juste besoin de lire la structure du graphe de la scène et de prendre leurs propres décisions sur ce qui doit être dessiné. Cela devrait, en théorie, augmenter l'utilisation de votre CPU dans le Gestionnaire des tâches. Ce ne sera pas 100% par la suite, mais ce sera un premier pas très appréciable.
J'espère que cela clarifie les choses et explique également la préoccupation générale ici. Il suffit de dire que X-Plane 12 n'en est qu'à ses débuts et que, tout comme 11, il recevra des mises à jour une fois qu'il sera sorti de la phase bêta. Je ne peux pas vous donner un délai pour savoir quand il sera plus rapide, mais c'est une préoccupation constante à tout moment et il sera certainement plus rapide au cours de la v12.
Traduit avec www.DeepL.com/Translator (version gratuite)
... Comme d'autres l'ont mentionné de manière plus ou moins agréable, le problème ici est que votre CPU a plusieurs cœurs et Windows les additionne tous jusqu'à 100% du système. Ainsi, par exemple, si vous avez un système à 4 cœurs et qu'un seul est utilisé, Windows indiquera que l'utilisation du CPU est de 25%. D'autres systèmes d'exploitation rapportent l'utilisation du CPU différemment, par exemple macOS traitera chaque cœur séparément. Ainsi, sur une machine à 4 cœurs, l'utilisation du CPU peut atteindre 400 % et l'utilisation d'un seul cœur affichera une utilisation de 100 %.
Comme mentionné, la meilleure façon de savoir quelle partie du système est la plus sollicitée est de regarder les données de synchronisation fournies dans X-Plane. Vous obtenez un nombre pour le temps CPU et GPU, exprimé en secondes où la fraction est les millisecondes. Dans la capture d'écran ci-dessus, le temps CPU est de 0,0304 et le temps GPU est de 0,0250. Cela signifie que pour une seule image, X-Plane prend 30,4 millisecondes sur le CPU et 25 millisecondes sur le GPU. A partir de là, la conclusion est que le goulot d'étranglement est le CPU : Le GPU peut produire une image toutes les 25 millisecondes alors que le CPU peut en produire une toutes les 30 millisecondes. Pour augmenter le nombre d'images par seconde, il faut donc décharger un peu le CPU pour réduire le temps qu'il met. Comme point de référence, vous pouvez faire 1 / temps pour obtenir les FPS (parce qu'il mesure les images par _une_ seconde, donc c'est l'inverse du temps pour une seule image. Alternativement, 1 / FPS vous donne le temps maximum qu'une seule image peut prendre pour atteindre ce FPS). Dans votre cas, le GPU pourrait fonctionner à 1 / 0,025 = 40 FPS. Si vous visez 60 FPS, alors vous voudriez 1 / 60 = 0,016 comme objectif de temps de trame, ou 16 millisecondes maximum par trame.
En fin de compte, vous avez absolument raison, X-Plane laisse des ressources CPU sur la table dans ce cas.
Alors, laissez-moi prendre la tangente et expliquer pourquoi X-Plane n'utilise pas tous les cœurs du CPU à tout moment :
De manière inhérente, tous les programmes tournent par défaut sur un cœur de CPU, toutes les instructions s'exécutent linéairement (ne me faites pas le coup de l'ackshually sur les architectures super scalaires hors d'usage). Et pour tirer parti d'un autre cœur, le programme doit être écrit dans cet esprit, puis bifurquer de son exécution et faire tourner un autre thread sur l'autre cœur de CPU qui lui-même exécute aussi des instructions linéairement, mais probablement des instructions différentes. C'est là le cœur du problème : le programme doit avoir suffisamment de travail pour pouvoir être séparé et exécuté en parallèle avec le reste des autres cœurs. Il est difficile d'y parvenir, car il faut ajouter un certain nombre d'éléments supplémentaires au code : Par exemple, puisque les threads fonctionnent en parallèle sans garantie d'ordre, si vous avez besoin d'accéder à des données qui sont en train d'être écrites par un autre thread, vous devrez commencer à synchroniser les deux threads, ce qui signifie potentiellement qu'un thread doit attendre l'autre, gaspillant des cycles de CPU en ne faisant rien. Il est en fait assez facile de prendre naïvement un programme à un seul thread et d'écrire un code multithread qui est moins performant à cause de cela. Il n'est pas non plus très facile d'écrire du code multithread pour commencer, il n'y a pas de véritables sauvegardes et, comme il n'y a pas de garanties d'ordre, les bogues peuvent être extrêmement insidieux : une course aux données où deux threads ne sont pas correctement synchronisés peut fonctionner 99% du temps, puis, une fois le code envoyé aux clients, il commence à s'effondrer et à se planter de manière mystérieuse sans que l'on sache vraiment pourquoi. Gardez à l'esprit qu'il est facile d'écrire "vous devez synchroniser vos threads correctement" sur le papier, mais que c'est une toute autre affaire lorsque vous avez un programme de plusieurs millions de lignes de code où il devient beaucoup plus difficile de raisonner sur le code qui s'exécute dans quel contexte et dans quelles conditions. Cela s'aggrave lorsque vous avez une base de code qui, à l'origine, n'a pas été écrite pour être multithreadée pour des raisons historiques et qui doit ensuite être adaptée pour ce travail.
Il y a encore un autre problème ici. Outre la barrière à l'entrée pour écrire du code multithread, le problème suivant est que vous devez décomposer votre code en morceaux qui peuvent s'exécuter indépendamment. Certains problèmes sont gênants du point de vue du parallélisme, qui est le nom réel d'une catégorie de code ou d'algorithmes qui s'adaptent extrêmement bien à plusieurs threads. C'est le cas, par exemple, du traitement parallèle des données, où chaque thread peut lire un sous-ensemble de données et le traiter indépendamment. D'autres choses ne se prêtent pas aussi bien au multithreading, par exemple le rendu : Les commandes de rendu sont encodées dans un tampon de commande pour être exécutées par le GPU. Cet encodage doit être effectué dans l'ordre. Vous ne pouvez donc pas vous contenter d'un traitement parallèle, car vous devrez très rapidement remettre les résultats dans l'ordre, ce qui est extrêmement coûteux et lourd en termes de synchronisation. Pour X-Plane par exemple, nous avons la mise à jour de la physique : la mise à jour de la physique doit être exécutée en premier pour que nous puissions résoudre la position de la caméra, ensuite nous pouvons commencer le rendu car nous savons où nous sommes dans le monde. D'autre part, le moteur physique peut absolument calculer le modèle de vol de plusieurs avions en parallèle, car ils sont indépendants les uns des autres.
Les moteurs de jeu de la vieille école ont résolu ce problème en divisant le code en sous-catégories. Ainsi, par exemple, un thread s'occupait du rendu, un autre de la physique, un autre de l'audio et ainsi de suite. Le problème est que vous allez très vite manquer de catégories de haut niveau que vous pouvez décomposer et dans un monde de 32 cœurs ou plus, cette stratégie tombe à plat. Sans compter que si vous concevez un moteur pour 32 cœurs de cette manière, les utilisateurs disposant de moins de cœurs auront beaucoup de mal. L'approche moderne de ce problème est d'avoir un planificateur de tâches et d'exécuter un tas de petites tâches en parallèle, puis de synchroniser le travail aussi rarement que possible. Dans un tel système, vous pourriez avoir des mises à jour d'objets qui sont toutes lancées comme leurs propres tâches pour être traitées en parallèle, puis synchronisées pour être rendues correctement. Cette approche s'adapte très bien à de nombreux cœurs de processeur, car le planificateur de tâches n'a qu'à créer plus de threads et à répartir les tâches entre eux. Tant qu'il y a des tâches disponibles pour occuper tous les cœurs du processeur, vous êtes gagnant.
C'est exactement ce que fait aussi X-Plane. Cependant, la charge de travail qui est jobifiée est limitée dans X-Plane. En général, le chargement en arrière-plan est ce que X-Plane fait en parallèle. Ainsi, la pagination des nouvelles ressources artistiques et des données de terrain est effectuée en arrière-plan. C'est assez facile à synchroniser car tout ce que vous avez à faire est de ne pas utiliser les ressources artistiques avant qu'elles ne soient prêtes, il n'y a pas de consommateurs partagés de quelque chose qui vient d'être créé. Cependant, beaucoup de choses qui se passent d'une image à l'autre ne sont pas encore parallèles. Cela est principalement dû à des décisions de conception héritées qui ont été intégrées dans le moteur d'une manière ou d'une autre. Tout ce bazar doit être nettoyé avant que nous puissions commencer à paralléliser la trame. Ce travail figure en tête de notre liste de priorités, car rendre X-Plane plus rapide est un objectif assez important, mais c'est un travail facile et le bénéfice n'est pas là tant que tout n'est pas terminé. L'un de nos premiers objectifs est d'exécuter le rendu des ombres et le rendu de l'image principale en parallèle. En théorie, ils sont indépendants l'un de l'autre car ils ont juste besoin de lire la structure du graphe de la scène et de prendre leurs propres décisions sur ce qui doit être dessiné. Cela devrait, en théorie, augmenter l'utilisation de votre CPU dans le Gestionnaire des tâches. Ce ne sera pas 100% par la suite, mais ce sera un premier pas très appréciable.
J'espère que cela clarifie les choses et explique également la préoccupation générale ici. Il suffit de dire que X-Plane 12 n'en est qu'à ses débuts et que, tout comme 11, il recevra des mises à jour une fois qu'il sera sorti de la phase bêta. Je ne peux pas vous donner un délai pour savoir quand il sera plus rapide, mais c'est une préoccupation constante à tout moment et il sera certainement plus rapide au cours de la v12.
Traduit avec www.DeepL.com/Translator (version gratuite)