Alternative a foreach iterators che coinvolgono ref e out

3

Sto cercando di creare un sistema di particelle flessibile per il mio gioco XNA, e ho queste interfacce:

public interface IParticle : IUpdateable
{
    bool Alive { get; }
    float Percent { get; }
}

public interface IParticleEffect<T>
    where T : IParticle
{
    void Apply(GameTime time, ref T particle);
}

public interface IParticleEmitter<T> : IUpdateable
    where T : IParticle
{
}

public interface IParticleRenderer<T> : IDrawable
    where T : IParticle
{
}

L'idea alla base di questo sistema è che il codice cliente deve solo derivare da IParticle , quindi creare una sottoclasse compatibile da IParticleEmitter e IParticleRenderer , e tutto il resto funziona solo automagicamente dietro le quinte. (In realtà sto scrivendo tutto al momento, ma gli ultimi due avrebbero una classe base astratta disponibile.)

Ad ogni modo, alcuni sistemi particellari amano usare le strutture mutevoli per scopi di ottimizzazione, ed è perfettamente ragionevole. Il mio sistema fornisce solo lo scheletro, e se il cliente decide che "Ehi, le strutture sono la strada da percorrere!", Allora il mio sistema dovrebbe supportare qualsiasi cosa il codice client gli getti. Questo è il motivo per cui il mio metodo IParticleEffect.Apply() prende una particella per ref - è più economico passare una struct per riferimento piuttosto che copiarlo.

Sfortunatamente, si interrompe quando sono coinvolte le raccolte, perché l'iteratore foreach non gioca bene con gli oggetti passati da ref o out . Eric Lippert spiega perché qui .

Quindi, ora ho una decisione sul design da fare:

  1. Ignora completamente le strutture e modifica il mio vincolo in where T: class, IParticle . Ciò potrebbe danneggiare le future ottimizzazioni, ma rende molto più semplice lavorare con le raccolte.
  2. Cambia tutto ciò che usa ICollection<T> o IEnumerable<T> in IList<T> così posso eseguire il polling manualmente tramite un indicizzatore. Ciò lo rende potenzialmente più potente, ma al costo di utilizzare un'interfaccia più profonda (elenco) per memorizzare i miei oggetti.
  3. Qualcos'altro

Spero che questa domanda non sia troppo "dipende", ma sono curioso di sapere quali strategie posso applicare qui per farlo funzionare nel modo che voglio.

EDIT : mi sono reso conto che potevo includere anche una variabile locale come:

foreach (var particle in SomeParticleCollection)
{
    var p = particle;
    SomeEffect.Apply(ref p);
}

Tuttavia, p avrebbe comunque l'effetto netto di copiarlo, anche questo non è l'ideale.

    
posta Kyle Baran 18.06.2014 - 06:50
fonte

3 risposte

3

L'ottimizzazione prematura è malvagia.

Rilascia le strutture - scegli un design flessibile. Il tuo progetto iniziale sembra che tu stia consentendo flessibilità / estensibilità. Finché non ti preoccupi delle prestazioni, mantieni l'architettura flessibile. Dovresti fare un serio profiling delle prestazioni per convincere tutti che il superamento delle strutture rispetto agli oggetti è il più grande drenaggio delle prestazioni del tuo codice - per quanto riguarda il rendering, la logica di aggiornamento, l'intelligenza artificiale ecc.

Ecco un articolo su come / come ottimizzare le architetture di programmazione di gioco per le prestazioni che ho trovato una buona lettura: link

    
risposta data 18.06.2014 - 08:13
fonte
2

In secondo piano JBRWilkinson nel suggerimento di eliminare le strutture.

Oltre a ciò che è stato scritto in quella risposta, avere un struct implement IUpdateable implica che i bisogni della struct devono essere mutabili, il che può invitare una certa classe di bug. Le strutture, che si trovano sullo heap, vengono assegnate per copia non per riferimento, quindi qualsiasi modifica apportata a una copia non verrà riportata nel database degli oggetti di gioco. Cioè se scrivi codice come

    foreach (var particle in SomeParticleCollection)
    {
        particle.EnabledChanged += myEvent;
    }

in realtà non può fare nulla, perché la struct "particle" sarà una copia della struct nella collezione. Allo stesso modo, chiunque implementa un IParticleEffect che modifica l'IParticle in arrivo sarà deluso nonostante sia passato per riferimento.

(Sospetto che se un qualsiasi sistema utilizza le strutture mutabili per implementare IUpdateable, in realtà lo tengono in box per la maggior parte del tempo. Ciò sembrerebbe vanificare lo scopo dell'ottimizzazione, a meno che mi manchi qualcosa.)

Per ulteriori informazioni, consulta qui e, forse, qui

    
risposta data 18.06.2014 - 08:56
fonte
2

Sei sicuro di aver davvero bisogno di un simile tipo di ottimizzazione ( ref T )? Hai considerato sistemi analoghi o deriso i tuoi? L'ottimizzazione prematura è peggio di nessuna - si finisce con decisioni che non sono ovvie nel migliore dei casi e incomprensibili nel peggiore dei casi.

ref T indica che il valore di riferimento potrebbe essere modificato dalla funzione. E non penso che sia un comportamento normale per IParticleEffect<T> . Sembra strano.

Stai definendo il tuo sistema e qualsiasi sistema di particelle estranee dovrà essere inserito in modo speciale in esso. Nel peggiore dei casi, se si riscontra che la copia della struttura riduce notevolmente le prestazioni, è possibile provare a racchiuderla in una variabile di classe.

public class ParticleStructWrapper<T> : IParticle
 where T: struct, IParticle
{
   T _Particle;
   public ParticleStructWrapper(T particle) {...}

   public Boolean IsAlive { get {return this._Particle.IsAlive;}}
   ...
}

causerà comunque una copia per il costruttore ma se lo userai più volte l'effetto netto sarà ridotto.

Ma se le tue particelle originali devono essere cambiate in IParticleEffect, allora dovresti usare la tua soluzione originale (anche se non è molto semplice) o creare un proxy più sofisticato.

DOPO

the intent was to have the effect modify the particle

L'idea principale dietro le strutture è che consentono il passaggio semplice in base al valore: per le strutture con solo i campi struct, tale passaggio rende la copia effettiva effettiva. Con tali strutture non devi temere che qualcuno lo modifichi all'interno del metodo, perché la tua struttura originale è completamente inalterata. Per le piccole strutture la copia per valore e l'uso successivo sono anche più veloci a causa del fatto che le strutture si trovano nello stack.

Non puoi cambiare quei sistemi di particelle esterne, ma potresti raccomandare ai loro sviluppatori di leggere link , link .

P.S .: È possibile che abbiano già considerato tutti i pro e i contro tra classi e strutture, ma la loro soluzione non è la più facile da usare, o le particelle sono progettate per essere immutabili.

    
risposta data 18.06.2014 - 08:16
fonte

Leggi altre domande sui tag