Calculs de gravité 2D multithreading


24

Je suis en train de construire un jeu d'exploration spatiale et j'ai actuellement commencé à travailler sur la gravité (en C # avec XNA).

La gravité doit encore être modifiée, mais avant de pouvoir le faire, je dois résoudre certains problèmes de performances avec mes calculs physiques.

Cela utilise 100 objets, ce qui rend normalement 1000 d'entre eux sans calculs physiques dépasse bien 300 FPS (ce qui est mon plafond FPS), mais plus de 10 objets environ amènent le jeu (et le seul thread sur lequel il s'exécute) à son genoux lors des calculs de physique.

J'ai vérifié l'utilisation de mon fil et le premier fil se tuait de tout le travail, alors j'ai pensé que je devais juste faire le calcul physique sur un autre fil. Cependant, lorsque j'essaie d'exécuter la méthode Update de la classe Gravity.cs sur un autre thread, même si la méthode Update de Gravity n'a rien dedans, le jeu est toujours à 2 FPS.

Gravity.cs

public void Update()
    {
        foreach (KeyValuePair<string, Entity> e in entityEngine.Entities)
        {
            Vector2 Force = new Vector2();

            foreach (KeyValuePair<string, Entity> e2 in entityEngine.Entities)
            {
                if (e2.Key != e.Key)
                {
                    float distance = Vector2.Distance(entityEngine.Entities[e.Key].Position, entityEngine.Entities[e2.Key].Position);
                    if (distance > (entityEngine.Entities[e.Key].Texture.Width / 2 + entityEngine.Entities[e2.Key].Texture.Width / 2))
                    {
                        double angle = Math.Atan2(entityEngine.Entities[e2.Key].Position.Y - entityEngine.Entities[e.Key].Position.Y, entityEngine.Entities[e2.Key].Position.X - entityEngine.Entities[e.Key].Position.X);

                        float mult = 0.1f *
                            (entityEngine.Entities[e.Key].Mass * entityEngine.Entities[e2.Key].Mass) / distance * distance;

                        Vector2 VecForce = new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
                        VecForce.Normalize();

                        Force = Vector2.Add(Force, VecForce * mult);
                    }
                }
            }

            entityEngine.Entities[e.Key].Position += Force;
        }

    }

Ouais je sais. C'est une boucle imbriquée foreach, mais je ne sais pas comment faire le calcul de la gravité, et cela semble fonctionner, c'est tellement intensif qu'il a besoin de son propre thread. (Même si quelqu'un connaît un moyen super efficace de faire ces calculs, j'aimerais quand même savoir comment je pourrais le faire sur plusieurs threads à la place)

EntityEngine.cs (gère une instance de Gravity.cs)

public class EntityEngine
{
    public Dictionary<string, Entity> Entities = new Dictionary<string, Entity>();
    public Gravity gravity;
    private Thread T;


    public EntityEngine()
    {
        gravity = new Gravity(this);
    }


    public void Update()
    {
        foreach (KeyValuePair<string, Entity> e in Entities)
        {
            Entities[e.Key].Update();
        }

        T = new Thread(new ThreadStart(gravity.Update));
        T.IsBackground = true;
        T.Start();
    }

}

EntityEngine est créé dans Game1.cs et sa méthode Update () est appelée dans Game1.cs.

J'ai besoin que mon calcul physique dans Gravity.cs s'exécute à chaque fois que le jeu est mis à jour, dans un thread séparé afin que le calcul ne ralentisse pas le jeu à un niveau horriblement bas (0-2) FPS.

Comment pourrais-je faire fonctionner ce filetage? (toutes les suggestions pour un système de gravité planétaire amélioré sont les bienvenues si quelqu'un les a)

Je ne cherche pas non plus une leçon sur la raison pour laquelle je ne devrais pas utiliser le filetage ou les dangers de l'utiliser de manière incorrecte, je cherche une réponse directe sur la façon de le faire. J'ai déjà passé une heure à googler cette question avec peu de résultats que j'ai compris ou qui m'ont été utiles. Je ne veux pas me montrer grossier, mais il semble toujours difficile en tant que noob de programmation d'obtenir une réponse directe et significative, je préfère généralement obtenir une réponse si complexe que je serais facilement en mesure de résoudre mon problème si je le comprenais, ou quelqu'un disant pourquoi je ne devrais pas faire ce que je veux faire et n'offrant aucune alternative (qui est utile).

Merci pour l'aide!

EDIT : Après avoir lu les réponses que j'ai obtenues, je vois que vous vous souciez vraiment et n'essayez pas simplement de cracher une réponse qui pourrait fonctionner. Je voulais tuer deux oiseaux avec une pierre (améliorer les performances et apprendre certaines bases du multlthreading), mais il semble que la plupart du problème réside dans mes calculs et que le filetage soit plus compliqué que cela ne vaut pour les augmentations de performances. Merci à tous, je vais relire vos réponses et essayer vos solutions quand j'aurai fini l'école, merci encore!


Que fait [votre système de mise à jour des threads décrit ci-dessus] maintenant (cela fonctionne-t-il)? Btw Je le démarrerais dès que possible dans le cycle de jeu - par exemple avant la mise à jour des entités.
ThorinII

2
Les appels Trig à l'intérieur de vos boucles imbriquées sont probablement le plus gros succès. Si vous pouvez trouver un moyen de les éliminer, ça va réduire le kde ce O(n^2)problème beaucoup.
RBarryYoung

1
En effet, les appels trigonométriques sont totalement inutiles : vous devez d'abord calculer un angle à partir d'un vecteur, puis l'utiliser pour générer un autre vecteur qui pointe dans la direction donnée. Ensuite, vous normalisez ce vecteur, mais puisqu'il sin² + cos² ≡ 1est déjà normalisé de toute façon! Vous auriez pu simplement utiliser le vecteur d'origine qui relie les deux objets qui vous intéressent et normaliser celui-ci. Aucun appel trig n'est nécessaire.
leftaroundabout

XNA n'est-il pas obsolète?
jcora

@yannbane, cette question n'ajoute rien d'utile à la discussion. Et non, le statut de XNA ne correspond à aucune définition de déprécié.
Seth Battin

Réponses:


36

Ce que vous avez ici est un algorithme O (n²) classique . La cause première de votre problème n'a rien à voir avec le threading et tout à voir avec le fait que votre algorithme a une grande complexité.

Si vous n'avez jamais rencontré de notation "Big O" auparavant, cela signifie essentiellement le nombre d'opérations nécessaires pour travailler sur n éléments (c'est l'explication super simplifiée). Vos 100 éléments exécutent 10000 fois la partie intérieure de votre boucle .

Dans le développement de jeux, vous voulez généralement éviter les algorithmes O (n²) , sauf si vous avez une petite quantité de données (et de préférence fixe ou plafonnée) et un algorithme très rapide.

Si chaque entité affectait toutes les autres, vous auriez nécessairement besoin d'un algorithme O (n²). Mais il semble que seules quelques entités interagissent réellement (en raison de if (distance < ...)) - vous pouvez donc réduire considérablement votre nombre d'opérations en utilisant quelque chose appelé " partitionnement spatial ".

Comme il s'agit d'un sujet assez détaillé et quelque peu spécifique au jeu, je vous recommande de poser une nouvelle question pour plus de détails. Allons-nous en...


L'un des principaux problèmes de performances de votre code est assez simple. C'est très lent :

foreach (KeyValuePair<string, Entity> e in Entities)
{
    Entities[e.Key].Update();
}

Vous effectuez une recherche de dictionnaire par chaîne, à chaque itération (plusieurs fois dans vos autres boucles), pour un objet que vous avez déjà!

Vous pouvez faire ceci:

foreach (KeyValuePair<string, Entity> e in Entities)
{
    e.Value.Update();
}

Ou vous pouvez le faire: (Personnellement, j'aime mieux cela, les deux devraient être à peu près à la même vitesse)

foreach (Entity e in Entities.Values)
{
    e.Update();
}

Une recherche de dictionnaire par chaîne est assez lente. Itérer directement sera beaucoup plus rapide.

Cependant, à quelle fréquence avez-vous réellement besoin de rechercher des éléments par leur nom? Par rapport à la fréquence à laquelle vous devez parcourir tous ces éléments? Si vous ne faites que rarement des recherches de nom, pensez à stocker vos entités dans un List(donnez-leur un Namemembre).

Le code que vous avez là-bas est relativement trivial. Je ne l'ai pas profilé, mais je parie que la plupart de votre temps d'exécution va aux recherches répétées de dictionnaire . Votre code pourrait bien être "assez rapide" simplement en corrigeant ce problème.

EDIT: Le prochain plus gros problème est probablement d'appeler Atan2puis de le reconvertir immédiatement en vecteur avec Sinet Cos! Utilisez directement le vecteur.


Enfin, abordons le filetage et les principaux problèmes de votre code:

Tout d'abord et le plus évidemment: ne créez pas de nouveau fil à chaque image! Les objets thread sont assez "lourds". La solution la plus simple serait d'utiliser simplement à la ThreadPoolplace.

Bien sûr, ce n'est pas si simple. Passons au problème numéro deux: ne touchez pas aux données sur deux threads à la fois! (Sans ajouter l'infrastructure de sécurité des threads appropriée.)

Vous écrasez la mémoire ici de la manière la plus horrible . Il n'y a pas de sécurité des fils ici. N'importe lequel des "" gravity.Update"threads que vous démarrez peut remplacer les données utilisées dans un autre thread à des moments inattendus. Pendant ce temps, votre fil principal touchera sans aucun doute toutes ces structures de données. Je ne serais pas surpris si ce code produisait des violations d'accès à la mémoire difficiles à reproduire.

Il est difficile de sécuriser quelque chose comme ce thread et cela peut augmenter considérablement les performances, ce qui en vaut souvent la peine.


Mais, vu que vous avez demandé (pas si bien) comment le faire de toute façon, parlons-en ...

Normalement, je recommanderais de commencer par pratiquer quelque chose de simple, où votre fil est essentiellement "tirer et oublier". Lecture audio, écriture de quelque chose sur le disque, etc. Les choses se compliquent lorsque vous devez réinjecter le résultat dans le thread principal.

Il existe essentiellement trois approches à votre problème:

1) Mettez des verrous autour de toutes les données que vous utilisez sur les threads. En C #, cela est rendu assez simple avec l' lockinstruction.

Généralement, vous créez (et conservez!) Un new objectspécifiquement pour le verrouillage afin de protéger un ensemble de données (c'est pour des raisons de sécurité qui n'apparaissent généralement que lors de l'écriture d'API publiques - mais bon style tout de même). Vous devez ensuite verrouiller votre objet verrou partout où vous accédez aux données qu'il protège!

Bien sûr, si quelque chose est "verrouillé" par un thread parce qu'il est en cours d'utilisation et qu'un autre thread essaie d'y accéder - ce deuxième thread sera alors obligé d'attendre que le premier thread soit terminé. Donc, à moins que vous ne sélectionniez soigneusement les tâches qui peuvent être effectuées en parallèle, vous obtiendrez essentiellement des performances à un seul thread (ou pire).

Donc, dans votre cas, cela ne sert à rien à moins que vous puissiez concevoir votre jeu de telle sorte qu'un autre code s'exécute en parallèle qui ne touchera pas votre collection d'entités.

2) Copiez les données dans le thread, laissez-les traiter, puis retirez à nouveau le résultat une fois terminé.

La façon exacte dont vous implémenterez cela dépendra de ce que vous faites. Mais évidemment, cela impliquera une opération de copie potentiellement coûteuse (ou deux) qui, dans de nombreux cas, sera plus lente que de simplement faire des choses à un seul thread.

Et, bien sûr, vous devez encore avoir un autre travail à faire en arrière-plan, sinon votre thread principal restera assis en attendant que votre autre thread se termine pour pouvoir recopier les données!

3) Utilisez des structures de données thread-safe.

Celles-ci sont un peu plus lentes que leurs homologues à filetage unique et souvent plus difficiles à utiliser que le simple verrouillage. Ils peuvent toujours présenter des problèmes de verrouillage (réduisant les performances à un seul thread) sauf si vous les utilisez avec précaution.


Enfin, comme il s'agit d'une simulation basée sur des trames, vous devrez faire attendre le thread principal pour que les autres threads fournissent leurs résultats, afin que le trame puisse être rendu et que la simulation puisse continuer. Une explication complète est vraiment trop longue à mettre ici, mais fondamentalement, vous voudrez apprendre à utiliser Monitor.Waitet Monitor.Pulse. Voici un article pour commencer .


Je sais que je n'ai pas donné de détails d'implémentation spécifiques (sauf le dernier bit) ou de code pour aucune de ces approches. Tout d'abord, il y aurait beaucoup à couvrir. Et, deuxièmement, aucun d'entre eux n'est applicable à votre code à lui seul - vous devez aborder l'ensemble de votre architecture en vue d'ajouter du filetage.

L'enfilage ne fera pas comme par magie le code que vous avez là plus rapidement - il vous permet juste de faire autre chose en même temps!


8
+10 si je pouvais. Vous pouvez peut-être déplacer la dernière phrase vers le haut en guise d'introduction, car elle résume le problème principal ici. L'exécution de code sur un autre thread n'accélère pas comme par magie le rendu si vous n'avez rien d'autre à faire en même temps. Et le moteur de rendu attend probablement que le thread se termine, mais s'il ne le fait pas (et comment pourrait-il le savoir?), Il dessinera un état de jeu incohérent avec une physique d'entité encore à mettre à jour.
LearnCocos2D

Je suis profondément convaincu que le filetage n'est pas ce dont j'ai besoin, merci pour les informations longues et bien informées! En ce qui concerne les améliorations de performances, j'ai apporté les modifications que vous (et d'autres) avez suggérées, mais j'obtiens toujours de mauvaises performances en traitant> 60 objets. Je pense qu'il serait préférable pour moi de faire une autre question plus axée sur l'efficacité de la simulation N-Body. Vous obtenez ma réponse pour cela, cependant. Merci!
Postman

1
Vous êtes les bienvenus, heureux que cela ait aidé :) Lorsque vous postez votre nouvelle question, veuillez déposer un lien ici pour que moi, et toute autre personne suivante, la voie.
Andrew Russell

@Postman Bien que je sois d'accord avec ce que dit cette réponse en général, je pense qu'il manque complètement le fait qu'il s'agit essentiellement de l'algorithme PARFAIT pour tirer parti du threading. Il y a une raison pour laquelle ils font ces choses sur le GPU et c'est parce que c'est un algorithme trivialement parallèle si vous déplacez les écritures dans une deuxième étape. Il n'est pas nécessaire de verrouiller ou de copier ou de structures de données thread-safe. Un Parallel.ForEach simple et son fait sans aucun problème.
Chewy Gumball

@ChewyGumball Un point très valable! Et, bien que Postman doive rendre son algorithme biphasé, il devrait sans doute être biphasé de toute façon. Il convient de souligner, cependant, que ce Paralleln'est pas sans frais généraux, c'est donc certainement quelque chose à profiler - en particulier pour de si petits ensembles de données et (ce qui devrait être) un morceau de code relativement rapide. Et, bien sûr, il est sans doute préférable de réduire la complexité de l'algorithme dans ce cas - plutôt que de simplement y jeter du parallélisme.
Andrew Russell

22

D'accord, à première vue, vous devriez essayer certaines choses. Au début, vous devriez essayer de réduire vos contrôles de collision, vous pouvez le faire en utilisant une sorte de structure spatiale comme un quadtree . Cela vous permettra de réduire le deuxième nombre de foreach, car vous ne pourrez interroger que les entités fermant le premier.

Concernant votre thread: essayez de ne pas créer de thread à chaque tour de mise à jour. Ce surcoût ralentit peut-être davantage votre rythme que son accélération. Essayez plutôt de créer un seul thread de collision et laissez-le faire le travail pour vous. Je n'ai pas d' approche copier-coller-ce-code concrète , mais il y a des articles sur la synchronisation des threads et le travail en arrière-plan pour C #.

Un autre point est que dans la boucle foreach vous n'avez pas besoin de le faire entityEngine.Entities[e.Key].Texturecar vous avez déjà accédé au dict dans votre en-tête foreach. Au lieu de cela, vous pouvez simplement écrire e.Texture. Je ne connais pas vraiment l'impact de cela, je voulais juste vous le faire savoir;)

Une dernière chose: en ce moment, vous vérifiez chaque entité, car elle est interrogée dans la première ET la deuxième boucle foreach.

Exemple avec 2 entités A et B:

pick A in first foreach loop
   pick A in second foreach loop
      skip A because keys are the same
   pick B in second foreach loop
      collision stuff
pick B in first foreach loop
   pick A in second foreach loop
      collision stuff
   pick B in second foreach loop
      skip B because keys are the same

Bien qu'il s'agisse d'une approche possible, vous pouvez peut-être gérer A et B en un seul tour, en ignorant la moitié de vos vérifications de collision

J'espère que cela vous aidera à démarrer =)

PS: Même si vous avez dit que vous ne vouliez pas l'entendre: essayez de garder la détection de collision dans le même thread et accélérez-la juste assez. Enfiler cela semble être une bonne idée mais avec cela vient le besoin de se synchroniser comme l'enfer. Si votre vérification de collision est plus lente que votre mise à jour (raison de l'enfiler), vous obtiendrez des problèmes et des erreurs, car la collision se déclenchera après le déplacement des navires et vice versa. Je ne veux pas vous décourager, c'est juste une expérience personnelle.

EDIT1: Liens avec le didacticiel QuadTree (Java): http://gamedev.tutsplus.com/tutorials/implementation/quick-tip-use-quadtrees-to-detect-l probable-collisions-in-2d-space/


10
La bonne chose à propos de l'utilisation des quadruples / octets pour la simulation de la gravité est qu'au lieu d'ignorer simplement les particules distantes, vous pouvez stocker la masse totale et le centre de masse de toutes les particules dans chaque branche de votre arbre et l'utiliser pour calculer l'effet gravitationnel moyen de toutes les particules de cette branche sur d'autres particules éloignées. C'est ce que l'on appelle l' algorithme Barnes-Hut , et c'est ce que les pros utilisent .
Ilmari Karonen

10

Honnêtement, la première chose à faire est de passer à un meilleur algorithme.

La parallélisation de votre simulation ne peut, même dans le meilleur des cas, l'accélérer que d'un facteur égal au nombre de processeurs × cœurs par processeur × threads par cœur disponibles sur votre système - soit entre 4 et 16 pour un PC moderne. (Le déplacement de votre code vers le GPU peut générer des facteurs de parallélisation beaucoup plus impressionnants, au prix d'une complexité de développement supplémentaire et d'une vitesse de calcul de base par thread plus faible.) Avec un algorithme O (n²), comme votre exemple de code, cela vous permettrait utilisez de 2 à 4 fois plus de particules que vous n'en avez actuellement.

À l'inverse, le passage à un algorithme plus efficace pourrait facilement accélérer votre simulation par, disons, un facteur de 100 à 10000 (nombres purement estimés). La complexité temporelle de bons algorithmes de simulation à n corps utilisant la subdivision spatiale est à peu près égale à O (n log n), qui est "presque linéaire", de sorte que vous pouvez vous attendre à presque le même facteur d'augmentation du nombre de particules que vous pouvez manipuler. De plus, ce serait encore utiliser un seul thread, donc il y aurait encore de la place pour parallélisation en plus de cela .

Quoi qu'il en soit, comme les autres réponses l'ont noté, l'astuce générale pour simuler efficacement un grand nombre de particules en interaction est de les organiser en quadtree (en 2D) ou en octree (en 3D). En particulier, pour simuler la gravité, l'algorithme de base que vous souhaitez utiliser est l' algorithme de simulation Barnes – Hut , dans lequel vous stockez la masse totale (et le centre de masse) de toutes les particules contenues dans chaque cellule de votre quad / octree et utilisez-le pour approximer l'effet gravitationnel moyen des particules de cette cellule sur d'autres particules éloignées.

Vous pouvez trouver de nombreuses descriptions et tutoriels sur l'algorithme Barnes – Hut par Google pour cela, mais en voici une agréable et simple pour vous aider à démarrer , tandis que voici une description d'une implémentation avancée utilisée pour la simulation GPU des collisions de galaxies.


6

Une autre réponse d'optimisation qui n'a rien à voir avec les threads. Désolé pour ça.

Vous calculez la distance () de chaque paire. Cela implique de prendre une racine carrée, ce qui est lent. Il implique également plusieurs recherches d'objet pour obtenir les tailles réelles.

Vous pouvez l'optimiser à l'aide de la fonction DistanceSquared () à la place. Précalculez la distance maximale à laquelle deux objets peuvent interagir, mettez-la au carré, puis comparez-la avec DistanceSquared (). Si et seulement si la distance au carré est dans le maximum, alors prenez la racine carrée et comparez-la avec la taille réelle des objets.

EDIT : Cette optimisation est principalement pour lorsque vous testez des collisions, ce que j'ai remarqué maintenant n'est pas réellement ce que vous faites (bien que vous le feriez sûrement à un moment donné). Il peut néanmoins être applicable à votre situation, si toutes les particules sont de taille / masse similaires.


Ouais. Cette solution peut convenir (perte de précision négligeable uniquement), mais rencontre des problèmes lorsque la masse des objets diffère beaucoup. Si la masse de certains objets est très énorme alors que la masse de certains objets est très petite, la distance maximale pour raisonnable est plus élevée. Par exemple, l'effet de la gravité terrestre sur une petite particule de poussière est négligeable pour la terre, mais pas pour la particule de poussière (sur une assez grande distance). Mais en fait, deux particules de poussière à la même distance ne s'influencent pas significativement.
SDwarfs

En fait, c'est un très bon point. J'ai mal interprété cela comme un test de collision, mais cela fait en fait le contraire: les particules s'influencent si elles ne se touchent pas.
Alistair Buxton

3

Je ne sais pas grand chose sur le threading, mais il semble que vos boucles prennent du temps, alors peut-être changer

i = 0; i < count; i++
  j = 0; j < count; j++

  object_i += force(object_j);

pour ça

i = 0; i < count-1; i++
  j = i+1; j < count; j++

  object_i += force(object_j);
  object_j += force(object_i);

pourrait aider


1
pourquoi cela aiderait-il?

1
Parce que les deux premières boucles font 10 000 itérations, mais les secondes boucles ne font que 4 950 itérations.
Buksy

1

Si vous avez déjà d'énormes problèmes avec 10 objets simulés, vous devrez optimiser le code! Votre boucle imbriquée ne provoquerait que 10 * 10 itérations dont 10 itérations sont ignorées (même objet), ce qui entraîne 90 itérations de la boucle interne. Si vous n'atteignez que 2 FPS, cela signifie que vos performances sont si mauvaises que vous n'obtenez que 180 itérations de la boucle intérieure par seconde.

Je vous suggère de faire ce qui suit:

  1. PREPARATION / BENCHMARKING: Pour savoir sûrement que cette routine est le problème, écrivez une petite routine de référence. Il doit exécuter la Update()méthode de la gravité plusieurs fois, par exemple 1000 fois et mesurer son temps. Si vous voulez atteindre 30 FPS avec 100 objets, vous devez simuler 100 objets et mesurer le temps pour 30 exécutions. Elle devrait être inférieure à 1 seconde. L'utilisation d'une telle référence est nécessaire pour effectuer des optimisations raisonnables. Sinon, vous obtiendrez probablement le contraire et ralentirez le code car vous pensez qu'il doit être plus rapide ... Je vous encourage donc vraiment à le faire!

  2. OPTIMISATIONS: Bien que vous ne puissiez pas faire grand-chose sur le problème d'effort O (N²) (ce qui signifie que le temps de calcul augmente de façon quadratique avec le nombre d'objets simulés N), vous pouvez améliorer le code lui-même.

    a) Vous utilisez beaucoup de recherches de "tableau associatif" (Dictionnaire) dans votre code. Ce sont lents! Par exemple entityEngine.Entities[e.Key].Position. Ne pouvez-vous pas simplement utiliser e.Value.Position? Cela permet d'économiser une recherche. Vous faites cela partout dans toute la boucle interne pour accéder aux propriétés des objets référencés par e et e2 ... Changez ça! b) Vous créez un nouveau vecteur à l'intérieur de la boucle new Vector2( .... ). Tous les "nouveaux" appels impliquent une allocation de mémoire (et plus tard: la désallocation). Celles-ci sont même beaucoup plus lentes que les recherches de dictionnaires. Si vous n'avez besoin que de ce vecteur temporairement, allouez-le donc en dehors des boucles ET-réutilisez-le en réinitialisant ses valeurs aux nouvelles valeurs au lieu de créer un nouvel objet. c) Vous utilisez beaucoup de fonctions trigonométriques (par exemple atan2etcos ) dans la boucle. Si votre précision n'a pas besoin d'être vraiment exacte, vous pouvez essayer d'utiliser une table de recherche à la place. Pour ce faire, vous mettez votre valeur à l'échelle dans une plage définie, l'arrondissez à une valeur entière et la recherchez dans un tableau de résultats pré-calculés. Si vous avez besoin d'aide à ce sujet, demandez-le. d) Vous utilisez souvent .Texture.Width / 2. Vous pouvez pré-calculer cela et stocker le résultat sous la forme .Texture.HalfWidthou -si c'est toujours une valeur entière encore positive - vous pouvez utiliser bit l'opération de décalage>> 1 diviser par deux.

Effectuez une seule des modifications à la fois et mesurez la modification par le benchmark pour voir comment cela a affecté votre runtime! Peut-être qu'une chose est bonne alors que l'autre idée était mauvaise (même si je les ai proposées ci-dessus!) ...

Je pense que ces optimisations seront bien meilleures que d'essayer d'obtenir de meilleures performances en utilisant plusieurs threads! Vous aurez beaucoup de mal à coordonner les threads, afin qu'ils n'écrasent pas les autres valeurs. Ils entreront également en conflit lors de l'accès à des régions de mémoire similaires. Si vous utilisez 4 CPU / Threads pour ce travail, vous ne pouvez vous attendre qu'à une vitesse de 2 à 3 pour la fréquence d'images.


0

Pouvez-vous le retravailler sans les lignes de création d'objet?

Vector2 Force = nouveau Vector2 ();

Vector2 VecForce = nouveau Vector2 ((float) Math.Cos (angle), (float) Math.Sin (angle));

si vous pouvez peut-être placer la valeur de force dans l'entité au lieu de créer deux nouveaux objets à chaque fois, cela peut aider à améliorer les performances.


4
Vector2dans XNA est un type de valeur . Il n'a pas de frais généraux GC et les frais généraux de construction sont négligeables. Ce n'est pas la source du problème.
Andrew Russell

@Andrew Russell: Je ne suis pas sûr, mais est-ce vraiment toujours le cas si vous utilisez le "nouveau Vector2"? Si vous utilisez Vector2 (....) sans "nouveau", ce serait probablement différent.
SDwarfs

1
@StefanK. En C #, vous ne pouvez pas faire ça. A besoin du nouveau. Pensez-vous au C ++?
MrKWatkins
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.