Calcoli di gravità 2D multithreading


24

Sto costruendo un gioco di esplorazione dello spazio e attualmente ho iniziato a lavorare sulla gravità (in C # con XNA).

La gravità deve ancora essere modificata, ma prima di poterlo fare, devo affrontare alcuni problemi di prestazioni con i miei calcoli di fisica.

Questo utilizza 100 oggetti, di solito il rendering di 1000 di essi senza calcoli fisici supera i 300 FPS (che è il mio limite FPS), ma non più di 10 oggetti portano il gioco (e il singolo thread su cui gira) al suo ginocchia quando si eseguono calcoli di fisica.

Ho controllato il mio utilizzo del thread e il primo thread si stava uccidendo da tutto il lavoro, quindi ho pensato che dovevo solo fare il calcolo della fisica su un altro thread. Tuttavia, quando provo a eseguire il metodo di aggiornamento della classe Gravity.cs su un altro thread, anche se il metodo di aggiornamento di Gravity non contiene nulla, il gioco è ancora ridotto a 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;
        }

    }

Si lo so. È un ciclo foreach nidificato, ma non so come altro fare il calcolo della gravità, e questo sembra funzionare, è così intenso che ha bisogno del suo thread. (Anche se qualcuno conosce un modo super efficiente di fare questi calcoli, mi piacerebbe comunque sapere come avrei potuto farlo su più thread)

EntityEngine.cs (gestisce un'istanza di 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 viene creato in Game1.cs e il suo metodo Update () viene chiamato in Game1.cs.

Ho bisogno che il mio calcolo della fisica in Gravity.cs venga eseguito ogni volta che il gioco si aggiorna, in un thread separato in modo che il calcolo non rallenti il ​​gioco fino a un FPS orribilmente basso (0-2).

Come potrei fare per far funzionare questo threading? (eventuali suggerimenti per un sistema planetario a gravità migliorato sono ben accetti se qualcuno li possiede)

Inoltre non sto cercando una lezione sul perché non dovrei usare il threading o i pericoli di usarlo in modo errato, sto cercando una risposta diretta su come farlo. Ho già trascorso un'ora a cercare su Google questa domanda con pochi risultati che ho capito o che sono stati utili. Non intendo venire fuori maleducato, ma sembra sempre difficile come un noob programmatore ottenere una risposta chiara e significativa, di solito preferisco una risposta così complessa che potrei facilmente risolvere il mio problema se lo capissi, o qualcuno che dice perché non dovrei fare quello che voglio fare e non offre alternative (che sono utili).

Grazie per l'aiuto!

EDIT : Dopo aver letto le risposte che ho ricevuto, vedo che a voi ragazzi importa davvero e non state solo cercando di trovare una risposta che potrebbe funzionare. Volevo uccidere due uccelli con una fava (migliorare le prestazioni e apprendere alcune nozioni di base sul multlthreading), ma sembra che la maggior parte del problema risieda nei miei calcoli e che il threading sia più fastidioso di quanto valga per gli aumenti delle prestazioni. Grazie a tutti, rileggerò di nuovo le vostre risposte e proverò le vostre soluzioni quando avrò finito la scuola, grazie ancora!


Cosa fa ora [Il tuo sistema di threading degli aggiornamenti descritto sopra] (funziona)? Tra l'altro lo avvierei al più presto nel ciclo di gioco, ad esempio prima che le entità vengano aggiornate.
ThorinII

2
Le chiamate Trig all'interno dei tuoi loop nidificati sono probabilmente il più grande successo. Se riesci a trovare un modo per eliminarli, ciò ridurrà molto kquesto O(n^2)problema.
RBarryYoung

1
In effetti, le chiamate di trigger sono completamente inutili : prima si calcola un angolo da un vettore, quindi lo si utilizza per generare un altro vettore che punta nella direzione data. Quindi normalizzi quel vettore, ma dal momento sin² + cos² ≡ 1che è già normalizzato comunque! Avresti potuto semplicemente usare il vettore originale che collega i due oggetti che ti interessano e normalizzare questo. Non sono necessarie chiamate di emergenza.
lasciato circa l'

XNA non è deprecato?
jcora,

@yannbane questa domanda non aggiunge nulla di utile alla discussione. E no, lo stato di XNA non corrisponde a nessuna definizione di deprecato.
Seth Battin,

Risposte:


36

Quello che hai qui è un classico algoritmo O (n²) . La causa principale del tuo problema non ha nulla a che fare con il threading e tutto ha a che fare con il fatto che il tuo algoritmo ha un'alta complessità.

Se non ti sei mai imbattuto nella notazione "Big O" prima, significa sostanzialmente il numero di operazioni richieste per lavorare su n elementi (questa è la spiegazione super semplificata). I tuoi 100 elementi eseguono la parte interna del tuo loop 10000 volte .

Nello sviluppo del gioco in genere si desidera evitare algoritmi O (n²) , a meno che non si disponga di una piccola quantità di dati (e preferibilmente fissa o limitata) e di un algoritmo molto veloce.

Se ogni entità interessasse ogni altra entità, per necessità avresti bisogno di un algoritmo O (n²). Ma sembra che solo poche entità stiano effettivamente interagendo (a causa di if (distance < ...)) - quindi potresti ridurre significativamente il tuo numero di operazioni usando qualcosa chiamato " Partizionamento spaziale ".

Poiché si tratta di un argomento abbastanza dettagliato e in qualche modo specifico del gioco, ti consiglio di porre una nuova domanda per maggiori dettagli. Andiamo avanti...


Uno dei maggiori problemi di prestazioni con il tuo codice è abbastanza semplice. Questo è molto lento :

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

Stai facendo una ricerca nel dizionario per stringa, ogni iterazione (più volte negli altri loop), per un oggetto che hai già!

Potresti farlo:

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

Oppure potresti farlo: (personalmente mi piace di più, entrambi dovrebbero avere la stessa velocità)

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

Una ricerca nel dizionario per stringa è piuttosto lenta. L'iterazione diretta sarà significativamente più veloce.

Tuttavia, con quale frequenza devi effettivamente cercare gli articoli per nome? Rispetto alla frequenza con cui devi scorrere tutti? Se fai solo raramente ricerche sui nomi, prendi in considerazione la possibilità di archiviare le tue entità in un List(dai loro un Namemembro).

Il codice che hai effettivamente lì è relativamente banale. Non l'ho profilato, ma scommetto che gran parte del tuo tempo di esecuzione andrà alle ricerche ripetute del dizionario . Il tuo codice potrebbe essere "abbastanza veloce" solo risolvendo questo problema.

EDIT: Il prossimo problema più grande è probabilmente chiamare Atan2e poi convertirlo immediatamente in un vettore con Sine Cos! Usa direttamente il vettore.


Infine, affrontiamo il threading e i principali problemi nel tuo codice:

Innanzitutto e ovviamente: non creare un nuovo thread in ogni frame! Gli oggetti thread sono piuttosto "pesanti". La soluzione più semplice a questo sarebbe semplicemente usare ThreadPoolinvece.

Certo, non è così semplice. Passiamo al problema numero due: non toccare i dati su due thread contemporaneamente! (Senza aggiungere l'infrastruttura di sicurezza thread appropriata).

Fondamentalmente stai calpestando la memoria qui nel modo più orribile . Non c'è sicurezza del thread qui. Uno qualsiasi dei " gravity.Update" thread multipli che stai avviando può sovrascrivere i dati utilizzati in un altro thread in momenti imprevisti. Il tuo thread principale, nel frattempo, senza dubbio toccherà anche tutte queste strutture di dati. Non sarei sorpreso se questo codice producesse violazioni di accesso alla memoria difficili da riprodurre.

Rendere sicuro qualcosa di simile a questo thread è difficile e può aggiungere un notevole sovraccarico di prestazioni in modo che spesso non valga la pena.


Ma, visto che hai chiesto (non così) bene su come farlo comunque, parliamone ...

Normalmente consiglierei di iniziare praticando qualcosa di semplice, in cui il tuo thread è sostanzialmente "spara e dimentica". Riproduzione audio, scrittura di qualcosa su disco, ecc. Le cose si complicano quando si deve reinserire il risultato nel thread principale.

Esistono sostanzialmente tre approcci al tuo problema:

1) Metti i blocchi attorno a tutti i dati che usi tra i thread. In C # questo è reso abbastanza semplice con l' lockaffermazione.

Generalmente si crea (e si conserva!) Uno new objectspecifico per il blocco per proteggere alcuni set di dati (è per motivi di sicurezza che generalmente si presentano solo quando si scrivono API pubbliche, ma comunque di buon stile). Devi quindi bloccare il tuo oggetto di blocco ovunque acceda ai dati che protegge!

Naturalmente, se qualcosa è "bloccato" da un thread perché è in uso e un altro thread tenta di accedervi, quel secondo thread sarà quindi costretto ad attendere fino al termine del primo thread. Quindi, a meno che non selezioni attentamente le attività che possono essere eseguite in parallelo, otterrai prestazioni a thread singolo (o peggio).

Quindi, nel tuo caso, non ha senso farlo a meno che tu non possa progettare il tuo gioco in modo che qualche altro codice venga eseguito in parallelo che non toccherà la tua raccolta di entità.

2) Copia i dati nel thread, lasciali processare, quindi estrai di nuovo il risultato al termine.

Il modo esatto in cui lo implementerai dipenderà da cosa stai facendo. Ma ovviamente ciò comporterà un'operazione di copia (o due) potenzialmente costosa che in molti casi sarà più lenta del semplice fare cose a thread singolo.

E, naturalmente, devi ancora avere qualche altro lavoro da fare in background, altrimenti il ​​tuo thread principale sarà semplicemente in attesa di finire l'altro thread in modo che possa copiare i dati!

3) Utilizzare strutture di dati thread-safe.

Questi sono un po 'più lenti rispetto alle loro controparti a thread singolo e spesso più difficili da usare rispetto al semplice bloccaggio. Possono comunque presentare i problemi di blocco (riducendo le prestazioni fino a un singolo thread) a meno che non vengano utilizzati con attenzione.


Infine, poiché si tratta di una simulazione basata su frame, è necessario che il thread principale attenda che altri thread forniscano i loro risultati, in modo che il frame possa essere visualizzato e la simulazione possa continuare. Una spiegazione completa è davvero troppo lunga per essere inserita qui, ma fondamentalmente vorrai imparare come usare Monitor.Waite Monitor.Pulse. Ecco un articolo per iniziare .


So di non aver fornito dettagli di implementazione specifici (tranne l'ultimo bit) o ​​codice per nessuno di questi approcci. Prima di tutto, ci sarebbe molto da coprire. E, in secondo luogo, nessuno di questi è applicabile al tuo codice da solo - devi avvicinarti a tutta la tua architettura con uno sguardo verso l'aggiunta di thread.

Il threading non magicamente il codice che hai lì più velocemente, ti consente solo di fare qualcos'altro allo stesso tempo!


8
+10 se potessi. Forse puoi spostare l'ultima frase in alto come introduzione, perché riassume qui il problema principale. L'esecuzione di codice su un altro thread non velocizza magicamente il rendering se non hai nient'altro da fare allo stesso tempo. E il renderer probabilmente attende che il thread finisca, ma se non lo fa (e come potrebbe saperlo?) Trarrà uno stato di gioco incoerente con alcune entità fisiche ancora da aggiornare.
LearnCocos2D

Sono assolutamente convinto che il threading non sia quello di cui ho bisogno, grazie per le informazioni lunghe e ben informate! Per quanto riguarda i miglioramenti delle prestazioni, ho apportato le modifiche che tu (e altri) avete suggerito, ma sto ancora ottenendo cattive prestazioni quando mi occupo di> 60 oggetti. Penso che sarebbe meglio per me porre un'altra domanda più focalizzata sull'efficienza della simulazione N-Body. Ottieni la mia risposta per questo, però. Grazie!
Postino

1
Prego, sono contento che ti sia stato utile :) Quando pubblichi la tua nuova domanda, per favore lascia un link qui in modo che io e chiunque lo segua, lo vedremo.
Andrew Russell,

@Postman Mentre sono d'accordo con ciò che dice questa risposta in generale, penso che manchi completamente il fatto che questo è fondamentalmente l'algoritmo PERFETTO per trarre vantaggio dal threading. C'è un motivo per cui fanno queste cose sulla GPU ed è perché è un algoritmo banalmente parallelo se si spostano le scritture in un secondo passo. Non è necessario il blocco, la copia o il thread di strutture di dati sicure. Un semplice Parallel.ForEach ed è fatto senza problemi.
Chewy Gumball,

@ChewyGumball Un punto molto valido! E, mentre Postman avrebbe dovuto rendere il suo algoritmo in due fasi, probabilmente dovrebbe essere comunque in due fasi. Vale la pena sottolineare, tuttavia, che Parallelnon è privo di costi generali, quindi è sicuramente qualcosa da profilare, in particolare per insiemi di dati così piccoli e (ciò che dovrebbe essere) un pezzo di codice relativamente veloce. E, naturalmente, in questo caso è probabilmente ancora meglio ridurre la complessità dell'algoritmo, piuttosto che semplicemente lanciarci parallelismo.
Andrew Russell,

22

Ok a prima vista ci sono alcune cose che dovresti provare. All'inizio dovresti provare a ridurre i controlli delle collisioni, puoi farlo usando una sorta di struttura spaziale come un quadrifoglio . Ciò ti consentirà di ridurre il secondo conteggio foreach, poiché interrogherai solo le entità che chiudono il primo.

Per quanto riguarda il threading: cerca di non creare un thread ogni turno di aggiornamento. Questo sovraccarico forse sta rallentando più che accelerare le cose. Prova invece a creare un singolo thread di collisione e lascia che faccia il lavoro per te. Non ho un approccio concreto per copiare e incollare questo codice , ma ci sono articoli sulla sincronizzazione dei thread e sul background worker per C #.

Un altro punto è che nel ciclo foreach non è necessario che entityEngine.Entities[e.Key].Texturetu abbia già eseguito l'accesso al dict nell'intestazione foreach. Invece puoi semplicemente scrivere e.Texture. Non so davvero l'impatto di questo, volevo solo farti sapere;)

Un'ultima cosa: al momento stai ricontrollando ogni entità, perché viene interrogata nel primo e nel secondo ciclo foreach.

Esempio con 2 entità A e 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

Mentre questo è un possibile approccio, forse puoi gestire A e B in un turno, saltando metà dei controlli delle collisioni

Spero che questo ti inizi =)

PS: Anche se hai detto che non vuoi ascoltarlo: prova a mantenere il rilevamento delle collisioni nello stesso thread e accelera abbastanza. Il threading sembra una buona idea, ma con questo nasce la necessità di sincronizzarsi come l'inferno. Se il controllo delle collisioni è più lento dell'aggiornamento (motivo del threading), si verificheranno anomalie ed errori, poiché la collisione si innescherà dopo che le navi si sono già mosse e viceversa. Non voglio scoraggiarti, questa è solo un'esperienza personale.

EDIT1: collegamenti con il tutorial QuadTree (Java): http://gamedev.tutsplus.com/tutorials/implementation/quick-tip-use-quadtrees-to-detect-likely-collisions-in-2d-space/


10
La cosa bella dell'utilizzo di quad / octrees per la simulazione della gravità è che, invece di ignorare solo le particelle distanti, è possibile memorizzare la massa totale e il centro di massa di tutte le particelle in ciascun ramo dell'albero e utilizzarlo per calcolare l'effetto gravitazionale medio di tutte le particelle in questo ramo su altre particelle distanti. Questo è noto come algoritmo Barnes-Hut , ed è quello che usano i professionisti .
Ilmari Karonen,

10

Onestamente, la prima cosa da fare è passare a un algoritmo migliore.

Parallelizzare la tua simulazione può, anche nel migliore dei casi, accelerarla solo di un fattore pari al numero di CPU × core per CPU × thread per core disponibili sul tuo sistema, ovvero tra 4 e 16 per un PC moderno. (Lo spostamento del codice nella GPU può produrre fattori di parallelizzazione molto più impressionanti, a scapito della complessità di sviluppo aggiuntiva e di una velocità di calcolo della linea di base per thread inferiore.) Con un algoritmo O (n²), come il codice di esempio, ciò ti consentirebbe usa da 2 a 4 volte il numero di particelle che hai attualmente.

Al contrario, il passaggio a un algoritmo più efficiente potrebbe facilmente accelerare la simulazione di un fattore compreso tra 100 e 10000 (numeri puramente ipotizzati). La complessità temporale di buoni algoritmi di simulazione n-body che utilizzano la suddivisione spaziale si ridimensiona approssimativamente come O (n log n), che è "quasi lineare", in modo che ci si possa aspettare quasi lo stesso fattore di aumento del numero di particelle che è possibile gestire. Inoltre, sarebbe ancora utilizzando un solo filo, così ci sarebbe ancora spazio per parallelizzazione per di più .

Comunque, come hanno notato le altre risposte, il trucco generale per simulare in modo efficiente un gran numero di particelle interagenti è organizzarle in un quadrifoglio (in 2D) o in un ottetto (in 3D). In particolare, per simulare la gravità, l'algoritmo di base che si desidera utilizzare è l' algoritmo di simulazione Barnes-Hut , in cui si memorizza la massa totale (e il centro di massa) di tutte le particelle contenute in ciascuna cella del quad / octree e usalo per approssimare l'effetto gravitazionale medio delle particelle in quella cellula su altre particelle distanti.

Puoi trovare molte descrizioni ed esercitazioni sull'algoritmo Barnes – Hut di Googling , ma eccone una bella e semplice per iniziare , mentre ecco una descrizione di un'implementazione avanzata utilizzata per la simulazione GPU di collisioni di galassie.


6

Un'altra risposta di ottimizzazione che non ha nulla a che fare con i thread. Mi dispiace per quello.

Stai calcolando la distanza () di ogni coppia. Ciò implica prendere una radice quadrata, che è lenta. Comprende anche diverse ricerche di oggetti per ottenere le dimensioni effettive.

Puoi invece ottimizzarlo usando la funzione DistanceSquared (). Precalcolare la distanza massima alla quale possono interagire due oggetti, quadrarlo e quindi confrontarlo con DistanceSquared (). Se e solo se la distanza al quadrato è massima, prendi la radice quadrata e confrontala con le dimensioni reali dell'oggetto.

EDIT : Questa ottimizzazione è principalmente per quando stai testando le collisioni, che ora ho notato non è effettivamente quello che stai facendo (anche se sicuramente lo farai ad un certo punto). Potrebbe essere comunque applicabile alla tua situazione, se tutte le particelle sono di dimensioni / massa simili.


Si. Questa soluzione potrebbe andare bene (solo una perdita di precisione trascurabile), ma potrebbe creare problemi quando la massa degli oggetti differisce molto. Se la massa di alcuni oggetti è molto grande mentre la massa di alcuni oggetti è molto piccola, la distanza massima per ragionevole è maggiore. Ad esempio, l'effetto della gravità terrestre su una piccola particella di polvere è trascurabile per la terra, ma non per la particella di polvere (per una distanza abbastanza grande). Ma in effetti due particelle di polvere alla stessa distanza non si influenzano in modo significativo l'una con l'altra.
SDwarfs,

In realtà è un ottimo punto. Ho letto male questo come un test di collisione, ma in realtà sta facendo il contrario: le particelle si influenzano a vicenda se non si toccano.
Alistair Buxton,

3

Non so molto sul threading, ma sembra che i tuoi loop richiedano molto tempo, quindi forse cambiando da questo

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

  object_i += force(object_j);

a questa

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

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

potrebbe aiutare


1
perché sarebbe d'aiuto?

1
Perché i primi due loop fanno 10 000 iterazioni, ma i secondi loop eseguono solo 4 950 iterazioni.
Buksy,

1

Se hai già problemi così enormi con 10 oggetti simulati, dovrai ottimizzare il codice! Il tuo loop nidificato causerebbe solo 10 * 10 iterazioni di cui 10 iterazioni vengono ignorate (stesso oggetto), risultando in 90 iterazioni del loop interno. Se ottieni solo 2 FPS, ciò significherebbe che le tue prestazioni sono così cattive, che ottieni solo 180 iterazioni del loop interno al secondo.

Ti suggerisco di fare quanto segue:

  1. PREPARAZIONE / BENCHMARKING: per sapere sicuramente che questa routine è il problema, scrivere una piccola routine di benchmark. Dovrà eseguire il Update()metodo della gravità più volte, ad esempio 1000 volte, e misurare il suo tempo. Se si desidera ottenere 30 FPS con 100 oggetti, è necessario simulare 100 oggetti e misurare il tempo per 30 esecuzioni. Dovrebbe essere meno di 1 secondo. L'utilizzo di tale benchmark è necessario per fare ragionevoli ottimizzazioni. Altrimenti probabilmente otterrai il contrario e rallenterai il codice perché pensi che debba essere più veloce ... Quindi ti incoraggio davvero a farlo!

  2. OTTIMIZZAZIONI: Sebbene non si possa fare molto sul problema dello sforzo O (N²) (ovvero: il tempo di calcolo aumenta quadraticamente con il numero di oggetti simulati N), è possibile migliorare il codice stesso.

    a) Nel tuo codice utilizzi molte ricerche di "array associativo" (Dizionario). Questi sono lenti! Per esempio entityEngine.Entities[e.Key].Position. Non puoi semplicemente usare e.Value.Position? Questo salva una ricerca. Lo fai ovunque nell'intero ciclo interno per accedere alle proprietà degli oggetti a cui fanno riferimento e e e2 ... Cambia questo! b) Crei un nuovo vettore all'interno del loop new Vector2( .... ). Tutte le "nuove" chiamate implicano un po 'di allocazione della memoria (e successivamente: deallocazione). Questi sono anche molto più lenti delle ricerche sui dizionari. Se hai solo bisogno di questo Vector temporaneamente, quindi allocalo al di fuori dei loop E -utilizzalo- reinizializzando i suoi valori ai nuovi valori invece di creare un nuovo oggetto. c) Si utilizzano molte funzioni trigonometriche (ad es. atan2ecos) all'interno del loop. Se la tua precisione non ha bisogno di essere veramente esatta, potresti provare a usare una tabella di ricerca. Per fare ciò, ridimensionare il valore su un intervallo definito, arrotondarlo a un valore intero e cercarlo in una tabella di risultati precalcolati. Se hai bisogno di aiuto, basta chiedere. d) usi spesso .Texture.Width / 2. Puoi pre-calcolare questo e memorizzare il risultato come .Texture.HalfWidtho -se questo è sempre un valore intero anche positivo - puoi usare bit l'operazione di spostamento >> 1per dividere per due.

Esegui solo una delle modifiche alla volta e misura la modifica in base al benchmark per vedere come ha influito sul tempo di esecuzione! Forse una cosa è buona mentre l'altra è stata cattiva (anche io le ho proposte sopra!) ...

Penso che queste ottimizzazioni saranno molto meglio che cercare di ottenere prestazioni migliori usando più thread! Avrai molti problemi a coordinare i thread, quindi non sovrascriveranno gli altri valori. Inoltre, entreranno in conflitto anche quando si accede a aree di memoria simili. Se si utilizzano 4 CPU / thread per questo lavoro, ci si potrebbe aspettare solo una velocità da 2 a 3 per il frame rate.


0

Sei in grado di rielaborarlo senza le linee di creazione dell'oggetto?

Vector2 Force = new Vector2 ();

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

se forse potessi posizionare il valore di forza nell'entità invece di creare due nuovi oggetti ogni volta, ciò potrebbe aiutare a migliorare le prestazioni.


4
Vector2in XNA è un tipo di valore . Non ha spese generali GC e le spese generali di costruzione sono trascurabili. Questa non è la fonte del problema.
Andrew Russell,

@Andrew Russell: non ne sono così sicuro, ma è ancora così se usi "new Vector2"? Se usi Vector2 (....) senza "nuovo", questo sarebbe probabilmente diverso.
SDwarfs,

1
@StefanK. In C # non puoi farlo. Ha bisogno del nuovo. Stai pensando al C ++?
MrKWatkins,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.