Unit test su un renderer software, che si occupa dell'accoppiamento a causa di problemi di velocità

4

Il testing delle unità è qualcosa che amo ora dopo aver costretto me stesso a farlo nei progetti (e farlo sul lavoro) dopo aver visto le enormi ricompense che offre durante il refactoring e assicurare che le cose funzionino nel modo giusto.

In questo momento sto scrivendo un renderer software e non sono sicuro che ci sia un modo per configurarlo per essere testato. Ti darò un esempio di dove sono bloccato:

When scanning a polygon, it's most convenient right then and there after generating the scan to set the z-buffer and pixel from the texture as you're going along; when you're rendering a ton of polygons you need all the speed you can get.

The nice unit test way would be to return those scans so I could verify that each scan was where it was supposed to be, and check the data set along with it. Then for the next component that takes the scans, ...etc (more testing here)

Il problema qui è che tonnellate di scansioni di poligoni richiederanno molti intervalli per essere restituiti in vari casi, aggiungendo non solo un maggiore utilizzo della memoria, ma chiamate di funzioni aggiuntive che si ridurranno notevolmente quando gli utenti avranno risoluzioni più elevate. Facendo tutto in una volta, il renderer non soffoca, soprattutto nelle scene intensive di poligono.

Ho pensato ad alcuni possibili modi per aggirare questo problema, ma sembra che tutti abbiano il loro lato negativo:

  • Basta fare il modo ottimizzato e controllare i pixel finali alla fine di esso (che posso intercettare e controllare), ma poi se le cose si rompono ho intenzione di passare un sacco di tempo potenzialmente scoprendo esattamente dove qualcosa si è rotto.

  • Estendi le classi e ispeziona uno stub o in qualche modo intercettano i dati prima di passarli a disegnare su un buffer di pixel, tuttavia dovrò fare metodi virtuali (usando C ++) e questo introdurrà potenzialmente un sovraccarico Non ho bisogno attraverso un tavolo virtuale, a meno che non mi sbagli. Mi sto appoggiando a questo perché non so se il vtable sia effettivamente così costoso, ma potrei sbagliarmi quando si tratta del massiccio rendering di poligoni.

  • Mangia solo la penalizzazione delle prestazioni e forse alla fine del progetto, ottimizzalo dopo essere stato testato abbastanza, ma sembra che non sia molto TDD dal momento che il refactoring in un altro punto potrebbe creare problemi.

Voglio assicurarmi che tutti i miei elementi funzionino, ma finora devo raggrupparli insieme e fa sì che i test unitari non siano così appropriati ... poiché se qualcosa va storto, non so se il bordo del poligono lo scanner è rotto, o se l'algoritmo scanline è rotto, o il buffer z è stato impostato errato, o se la matematica per i prodotti vettoriali / incrociati / punti è sbagliata, ecc.

Inoltre non sono un fan di prendere uno screenshot alla fine e con un po 'di tolleranza per controllare se il renderer funziona correttamente (credo più di un test di integrazione). Probabilmente lo farò comunque, ma mi sembra troppo fragile, perché mi piace sapere "okay, questo sottomodulo è appena rotto" piuttosto che "l'intero oleodotto è appena rotto, prenderà il mio caffè e rimarrà comodo per le prossime ore cercando di scoprire dove ".

Supponendo che non mi manchi un po 'di "foresta dagli alberi" che è davvero ovvio, qual è il modo corretto per farlo?

    
posta Water 08.09.2016 - 20:28
fonte

2 risposte

4

Usa l'opzione 2 ("intercettare i dati prima di passarli al disegno su un buffer di pixel"), ma non con metodi virtuali. Utilizza invece un meccanismo tempo di compilazione che attiva solo "ispettore dati" o "registratore di dati" durante i test dell'unità.

Ad esempio, supponendo che i test dell'unità siano eseguiti solo in modalità "debug" dell'applicazione, utilizzare un'istruzione di preprocessore come #ifdef _DEBUG che aggiunge alcune chiamate di ispezione o di registrazione solo in modalità "debug". Quindi in modalità "rilascio", che utilizzi per distribuire il tuo codice finale, non avrai alcuna penalità sulle prestazioni. Se è necessario eseguire i test in "modalità rilascio" per alcuni motivi, è anche possibile introdurre una modalità "Test" specifica, che è quasi identica alla modalità "Rilascio", con tutti gli altri flag di compilazione e ottimizzazione identici, ma differisce solo in le chiamate di ispezione aggiunte.

Se non ti piacciono le macro del preprocessore, puoi anche realizzare la stessa cosa con la meta-programmazione dei modelli.

    
risposta data 08.09.2016 - 22:56
fonte
3

Non penso che ci sia un metodo "corretto" per tutto ciò che riguarda il runtime, ma posso dirti con cosa ho avuto fortuna.

Ho creato una classe Journal che è una classe base astratta. Lo passo con tutte le mie funzioni come il tuo codice per le scansioni di poligoni. Ogni volta che finisco una scansione, controllo se il diario che ho ricevuto non è nullo. Se non è nullo, chiamo una funzione su di esso per segnalare i risultati della scansione.

In questo modo, posso accedere a questi dati di test a livello di scansione. Creo semplicemente un'istanza Journal per raccoglierla. Quando si esegue il rendering normalmente, quel puntatore è nullo, quindi l'unico costo è la dichiarazione if extra necessaria.

Questo diario può essere passato alla tua funzione in qualsiasi modo tu ritenga ragionevole. Potresti passarlo come argomento di funzione. Potresti passarlo come globale. Potresti usare qualche esotica variabile con ambito dinamico se necessario. Qualunque approccio abbia più senso per il tuo particolare programma.

Se profili il tuo codice e scopri che le dichiarazioni if ti rallentano, ci sono alcune cose che puoi fare

  • Raccogli i dati del diario e scollegalo in blocchi di dimensioni convenienti. Questo può far risparmiare tempo perché può essere più economico salvare i dati di quanto non fosse per fare l'istruzione if (in alcune situazioni estreme)
  • Imposta un const bool all'inizio della funzione che memorizza se il journal è nullo o meno. Molto spesso un buon compilatore ottimizzante può vederlo e generare due copie del codice, una con l'inserimento nel journal e l'altra senza.
  • Come ultima risorsa, per il codice con le prestazioni più elevate è possibile duplicarlo: uno con l'inserimento nel journal senza (e quindi gestire le sfide di controllo della versione della duplicazione)
risposta data 08.09.2016 - 22:11
fonte

Leggi altre domande sui tag