Quanta logica può essere inserita in una classe di produzione di pool di oggetti prima che faccia più male che bene?

4

La lingua principale a cui sono interessato a fare il pooling di oggetti è AS3, che è noto per averne bisogno in molti casi. In passato ho sempre realizzato una nuova classe di fabbrica per ogni tipo che avrei provato a raggruppare, i parametri della sua funzione di "produzione di oggetti" erano gli stessi del costruttore di quel tipo. In sostanza:

static public function produce(<list of paramaters>):<type>
{
    if (m_arryPool.length)
    {
        var obj:<type> = m_arryPool.pop();
        // do something with the arguments passed to produce()
        return obj;
    }
    return new obj(<arguments passed to produce()>);
}

Ma recentemente ho cercato di pensare a un modo per evitare di riscrivere questo tipo di codice della piastra della caldaia per ogni singolo tipo che voglio raggruppare, ma probabilmente senza rinunciare all'idea di passare argomenti agli oggetti restituiti come nell'esempio sopra, e sicuramente senza mettere tanta funzionalità dinamica e roba lì dentro che finisce per rallentare troppo le cose.

Sono ancora un po 'verde per il pool di oggetti. So che una factory di pooling unica per tutti non funzionerà probabilmente in tutti i casi (il Function.apply() di AS3, ad esempio, non supporta i costruttori), ma quando arriva al punto che stai passando in così tanti argomenti alla funzione produce() , usando così tante variabili non tipizzate, passando attraverso così tanti dizionari e liste, ecc., che non ne vale la pena più?

So che questa domanda potrebbe essere leggermente soggettiva, ma credo che ci siano in gran parte risposte obiettive ad essa. Sono principalmente interessato a come questo si applicherebbe all'AS3, ma sono anche interessato a (e chiedere) una risposta generale. È il modo migliore per gestirlo in generale solo per andare avanti e scrivere il codice boilerplate per ogni tipo che si intende condividere o qualcosa del genere? Grazie!

    
posta Panzercrisis 03.10.2013 - 20:14
fonte

2 risposte

2

Avrai bisogno di un tipo di metodo di inizializzazione per ottenere gli argomenti dell'istanza. Potrebbe essere utile inserirlo in uno spazio dei nomi.
L'uso di un voodoo non specificato non è buono in quanto il codice non tipizzato è molto più lento - in alcuni casi stiamo parlando di ordini di grandezza (se lo fai, semplicemente non è importante avere una piscina in primo luogo). C'è semplicemente molto di overhead coinvolto in esso. Tuttavia, un'invocazione del metodo tipizzata o l'accesso al campo possono ottenere prestazioni vicino alle chiamate al metodo C ++ (è comunque previsto il fattore 2-3).

Quindi hai solo un metodo di inizializzazione e puoi avere un semplice costruttore.

Motivi per lasciare vuoto il costruttore:

  1. I costruttori non sono JITed su AVM2 . Qualsiasi cosa tu faccia ci sarà più lentamente.
  2. Se si dispone di costruttori vuoti, è conveniente pre-allocare istanze in lotti. Per uno, aiuta a prevenire la frammentazione della memoria. Ma anche questo è qualcosa che puoi fare mentre la tua app ha un carico di CPU basso. Dì all'inizio, ad esempio quando carichi le risorse, puoi utilizzare il tempo libero per preallocare alcune cose. E quando hai bisogno delle istanze, sono già lì, il che può comportare un'esperienza complessiva più fluida.

E come nota a margine: non spingere e pop - per alcuni motivi sono molto lenti, anche sugli array.

La cosa migliore è mantenere un indice per un'ampia dimensione fissa Vector . Richiede 4 byte per voce (a causa dei riferimenti a 32 bit di FlashPlayer). Quindi 50000 slot occuperanno una memoria di 200 KB, che in realtà non è molto paragonata alla quantità di suoni o trame della memoria quando non compressi in memoria. Tuttavia, se sai che meno va bene, allora vai con meno. Questo è qualcosa che dovrai profilare.

Questo è come apparirebbe:

var max:int = 50000;
var pool:Vector.<Type> = new Vector.<Type>(max, true);
var count:int = 0;
public function free(obj:Type):void {
    if (count < max - 1)
        pool[count++] = obj;
}
public function alloc():Type {
    return (count > 0) ? pool[--count] else new Type();
}

Infine, evita metodi statici o globali. Sono anche gli ordini di grandezza più lenti.

Il pool sarebbe un'istanza (un buon uso per un singleton) e chiunque sia responsabile per ottenere oggetti da esso o rilasciandoli dovrebbe avere un riferimento a quell'oggetto.

    
risposta data 09.10.2013 - 22:41
fonte
5

Non conosco AS3, ma posso offrirti alcuni consigli sul design. Se passi così tanti argomenti, potresti voler ripensare il tuo design.

Perché non separare la creazione e l'inizializzazione? Passi in un argomento che sarebbe l'id del tipo di cui hai bisogno. Ogni classe ha il proprio id di tipo statico per rappresentarlo, e puoi accedervi chiamando qualcosa come getID ().

Si utilizza l'id del tipo come hash o indice nel pool di oggetti per ottenere l'oggetto. Il pool per ciascun oggetto è contenuto all'interno di 1 indice di array dinamico tramite l'id del tipo.

L'id del tipo viene utilizzato come metodo per determinare l'oggetto. In una funzione separata, come la funzione chiamante, si chiama una funzione sull'oggetto per inizializzare l'oggetto.

Indipendentemente da ciò che decidi, dovresti stressare il test AC3 per impostare i limiti appropriati nel tuo codice prima di andare in diretta con una soluzione.

    
risposta data 07.10.2013 - 03:48
fonte

Leggi altre domande sui tag