Perché Farseer 2.x memorizza i provvisori come membri e non nello stack? (.NETTO)


10

AGGIORNAMENTO: questa domanda si riferisce a Farseer 2.x. Il nuovo 3.x non sembra farlo.

Al momento sto usando Farseer Physics Engine abbastanza ampiamente e ho notato che sembra memorizzare molti tipi di valore temporanei come membri della classe e non nello stack come ci si potrebbe aspettare.

Ecco un esempio dalla Bodyclasse:

private Vector2 _worldPositionTemp = Vector2.Zero;

private Matrix _bodyMatrixTemp = Matrix.Identity;
private Matrix _rotationMatrixTemp = Matrix.Identity;
private Matrix _translationMatrixTemp = Matrix.Identity;

public void GetBodyMatrix(out Matrix bodyMatrix)
{
    Matrix.CreateTranslation(position.X, position.Y, 0, out _translationMatrixTemp);
    Matrix.CreateRotationZ(rotation, out _rotationMatrixTemp);
    Matrix.Multiply(ref _rotationMatrixTemp, ref _translationMatrixTemp, out bodyMatrix);
}

public Vector2 GetWorldPosition(Vector2 localPosition)
{
    GetBodyMatrix(out _bodyMatrixTemp);
    Vector2.Transform(ref localPosition, ref _bodyMatrixTemp, out _worldPositionTemp);
    return _worldPositionTemp;
}

Sembra un'ottimizzazione manuale delle prestazioni. Ma non vedo come questo possa aiutare le prestazioni? (Semmai penso che danneggerebbe facendo oggetti molto più grandi).

Risposte:


6

Sebbene in .NET i tipi di valore siano archiviati nello stack, con un costo di allocazione minimo, tuttavia non elimina il costo di inizializzazione.

In questo caso abbiamo un set di funzioni che usano una o due matrici temporanee, il che comporterebbe l'inizializzazione di 16-32 float per chiamata. Anche se questo può sembrare insignificante, se i metodi sono usati abbastanza spesso (diciamo, migliaia e migliaia di volte per frame), l'overhead totale può avere un impatto significativo. Se una tale tecnica viene utilizzata sistematicamente in tutti questi metodi, il sovraccarico eliminato può essere considerevole.

Mentre l'uso di una tale tecnica elimina la capacità di fornire la sicurezza della filettatura a livello di oggetto, in genere non è saggio fornire tale garanzia a tale livello granulare.


Sei sicuro di questo? Ho pensato che potesse essere per evitare di chiamare i costruttori. Ma per i tipi di valore non è necessario chiamare un costruttore - incluso il costruttore senza parametri predefinito - se si intende impostare tutti i membri (o passarlo come outparametro). Sono abbastanza sicuro che l'intero punto di questa regola sia che il compilatore possa saltare l'azzeramento di quella memoria - giusto? (È davvero così lento spostare il puntatore dello stack?)
Andrew Russell,

Sorprendente, no? Sfortunatamente, se si controlla l'IL generato, le matrici temporanee vengono inizializzate. Alcuni test rapidi mostrano che la versione temp-membro è ~ 10-15% più veloce.
Jason Kozak,

1
Sono sbalordito . In "Capire le prestazioni di XNA Framework" (GDC2008) Shawn Hargreaves dice delle strutture: "[il JIT] di solito capirà : 'nella riga successiva imposta immediatamente tutti e tre i campi [del Vector3], quindi non ho nemmeno bisogno per inizializzarlo a zero '" . Da dove provengono le mie informazioni. Ma ascoltando di nuovo ora, dice solo "di solito". Il prossimo punto immediato nella presentazione è che JIT si comporta in modo diverso con il debugger collegato, influendo sulle prestazioni (come hai effettuato il test?). Inoltre: sta parlando del JIT qui, quindi forse l'IL rimane "bello" (verificabilità?).
Andrew Russell,

L'IL è stato ispezionato tramite Reflector e i test sono stati eseguiti al di fuori dell'IDE incorporato nella versione (su Windows, non ho più un abbonamento CC per testare)
Jason Kozak,

1
Sulla base di questo - mi chiedo se sarebbe meglio (e quanto sarebbe meglio) rendere quei membri temporanei static(e / o riutilizzarli in modo più aggressivo). Così com'è, per esempio, la Bodyclasse in Farseer ha circa 73 float di membri "non necessari".
Andrew Russell,

-1

Buona domanda. Sono un tipo C # /. NET piuttosto acuto e un po 'fuori di testa per le prestazioni, e questa mi sembra una decisione piuttosto strana. La prima cosa che mi viene in mente è che questo codice non è sicuro per i thread. Non so se questo è un problema in un sistema di fisica, ma la memorizzazione di dati temporanei al di fuori dell'ambito di un metodo è spesso una ricetta per il disastro.

Onestamente, se incontrassi regolarmente questo tipo di codice in un framework di terze parti, probabilmente proverei a trovare un altro framework.


3
Non risponde davvero alla domanda.
Brian Ortiz,

Sì, il meglio che potrei fare è confermare che non è pazzo, e non sembra esserci alcun beneficio reale se viene codificato in quel modo. L'unica scoperta del vero intento è chiedere al ragazzo che ha scritto il codice :).
Mike Strobel,

Grazie Mike. Sto iniziando a sospettare che lo sviluppatore originale sia quello pazzo, non io. Ma aiuta sempre a controllare;)
Andrew Russell,

La sicurezza del thread a volte può essere una costosa garanzia da fornire, soprattutto quando si scrive una libreria pesante di calcolo FP per una piattaforma che non utilizza le istruzioni SIMD.
Jason Kozak,

-1

Il GC sul 360 fondamentalmente esegue solo raccolte GEN 2, che sono costose, quindi le variabili temporanee che vengono create e rimosse da ogni fotogramma (come gli oggetti temp) fanno funzionare un'intera raccolta, il che uccide le prestazioni molto velocemente.

Ho il sospetto che lo abbiano fatto in questo modo per riutilizzare quell'oggetto e non averlo raccolto.


1
Ciò è accaduto anche a me, ma i membri temporanei sembrano essere tipi di valore, quindi non verranno comunque allocati nell'heap gestito.
Mike Strobel,

2
Giusto, ma solo se sono membri della classe. Se sono locali (nell'ambito del metodo), verrebbero allocati nello stack. La domanda è perché non hanno semplicemente seguito quella strada.
Mike Strobel,

1
@Blair - Secondo MSDN ( msdn.microsoft.com/en-us/library/bb203912.aspx ) Xbox360 utilizza .NET Compact Framework. Sembra che la differenza in GC sia correlata a ciò, quindi la perseguirei per ulteriori ricerche in materia.
Logan Kincaid,

1
@Blair: @Logan Kincaid è corretto. Il Garbage Collector di CF si comporta in modo diverso rispetto al normale framework. C'è un bel discorso sull'argomento in XNA Game Studio 3.0 Unleashed - tuttavia quel libro sarà presto obsoleto con la versione 4.0.
Steven Evers,

1
@Blair: a causa del limitato ambiente di memoria del 360 (e della maggior parte dei dispositivi destinati tramite CF), viene utilizzato un GC Mark & ​​Sweep. Di conseguenza, molte piccole allocazioni attiveranno una raccolta e il tempo di raccolta è relativo al numero di riferimenti. Molti dettagli qui: download.microsoft.com/.../Mobility/…
Jason Kozak,
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.