PDA

Visualizza la versione completa : [C++] piccola libreria grafica e template , policy e algoritmi


giuseppe500
17-05-2014, 16:43
ciao.
Volevo creare una piccola libreria grafica che utilizza per creare mesh e configurare oggetti opengl i template e le policy class , per via di definire algoritmi generici .

Subito ve lo dico sinceramente speravo di ottenere qualche beneficio prestazionale, ma poi ho capito che non è questo che si ottiene, infatti ho fatto delle prove con misure di chiamate a funzioni statiche,polimorfiche e normali, e ho visto che il guadagno è davvero misero, anche se le chiamate sono moltissime, dell ordine dei 20000 chiamate che possono esserci in opengl calcolate le entita grafiche per i 60 o piu frame al sec.

adesso che ho iniziato a leggere alcuni libri sui template(modern c++ programming e template the complete guide)ho capito che i template servono e danno la loro massima potenza nella configurazione tipo le policy class e le host class e nel fatto che l'algoritmo(ma è molto difficile da fare ho capito) puo essere applicato su svariate entità.

posto il codice semplificato al massimo per fare capire cio che voglio fare



//classe non template da usare nelle collezioni
#include "stdafx.h"

class Mesh
{
public:
Mesh(){};
int vba;
int texture;
std::function<void()> fnx;

void invoke()
{
fnx();
}

template<class cl,class fn>
void bind()
{
cl foo;
fnx = std::bind(&cl::draw, foo);

}
};
//classe host che genera la mesh
template <class shader>
class HostClass
{
public:
Mesh *pM;
void HostClass::Create(){

pM = new Mesh();

shader * pShader = new shader();
pM->vba = 1;
pM->texture = 2;

typedef void (shader::* f)();
f pf= &shader::draw;
shader sh;
shader* psh = &sh;

pM->bind<shader,f>();

}

Mesh* Get()
{
return pM;
}

};
//classe shader ho omesso tutte le proprietà di configurazione
class Shader{

public:
Shader(){};
void Shader::draw()
{

}

};







Passo al problema :
ho due classi mesh e effect, mesh contiene i dati dei triangoli, le normali ecc... e questi dati una volta creati in opengl sono accessibili come Glint(un int ala fine).
effect contiene l'effetto e i suoi parametri ovvero per farla breve un metodo di renderizazione.

quello che ho fatto è passare a mesh un parametro template effect per configurare la mesh al rendering dell'effect passato oltre ad altre configurazioni relative all' unione delle due classi, tutte le classi effect hanno la stessa interfaccia.

quello che vorrei e non riesco a fare è generare in una host class con mesh e effect una classe di un tipo specifico , diciamo MeshEffect(non template) che ha tutti i valori che alla fine sono degli int dell opengl e questo è molto facile, il problema è la funzione di disegno che è nell effect, tutti gli effect hanno infatti una funzione
void draw(void)
{....implementazione}
ho provato ad usare le function del namespace functional e bindarci la funzione draw delll'effect per creare una std::function compatibile con tutti i tipi di mesheffect e quindi permettermi di utilizzare liberamente liste e container in generale.
La procedura funziona , l'ho provata solo che function ha delle prestazioni davvero misere ma dell ordine delle 3 volte una chiamata normale mentre le funzioni virtuali hanno delle prestazione dello 0.4 piu lente delle funzini normali.
fine della fola ho si un buon metodo di configurazione ma ho delle prestazioni misere ed ero partito per ottenere le prestazioni.
esiste qualcosa , un functore o qualcosa del genere un po piu prestazionale?



domanda , esiste un modo per avere una function un po piu veloce ?

MItaly
18-05-2014, 23:19
La cosa che in assoluto costa meno è passare un functore come parametro template; in tal caso, il compilatore ha il "quadro completo", sa subito chi verrà chiamato e può fare inlining di tutto quel che crede necessario.

In alternativa, in genere puntatori a funzione, puntatori a funzione membro e funzioni virtuali costano poco di più; il costo del branch misprediction in genere è molto basso se si continua a chiamare la stessa funzione (il branch predictor impara rapidamente dove deve effettivamente andare), il costo principale è rappresentato dalla mancata opportunità di inlining, che, per funzioni molto piccole e ben espandibili in linea, può essere considerevole.

std::function, infine, è inevitabilmente "costosa", dato che, per ottenere l'effetto della type erasure, fa uso di doppia indirezione con di mezzo chiamate virtuali.

giuseppe500
19-05-2014, 10:44
La cosa che in assoluto costa meno è passare un functore come parametro template; in tal caso, il compilatore ha il "quadro completo", sa subito chi verrà chiamato e può fare inlining di tutto quel che crede necessario.

In alternativa, in genere puntatori a funzione, puntatori a funzione membro e funzioni virtuali costano poco di più; il costo del branch misprediction in genere è molto basso se si continua a chiamare la stessa funzione (il branch predictor impara rapidamente dove deve effettivamente andare), il costo principale è rappresentato dalla mancata opportunità di inlining, che, per funzioni molto piccole e ben espandibili in linea, può essere considerevole.

std::function, infine, è inevitabilmente "costosa", dato che, per ottenere l'effetto della type erasure, fa uso di doppia indirezione con di mezzo chiamate virtuali.

grazie MItaly.
Se passo un functore come parametro template(in che classe mesh?)pero' non posso inserire i miei oggetti mesh in un container(penso) o c'è il modo di farlo?senza usare pero' any di boost, posso incapsulare un functore in una classe tipo Mesh senza parametri template?Creando magari la classe mesh in una host class che ha il parametro template il functore .
Ma in pratica tutti i giri e rigiri sono per ottenere in qualche modo una type erasure e questo si puo fare coi template senza std::function o in un qualche modo piu prestazionale?
ripeto in base a test ,std::function è 4 volte piu lenta e questo per il mio lavoro non è accettabile, quindi probabilmente dovro cambiare il disegno delle classi, magari ritornando alle funzioni virtuali che sono molto piu prestaionali di std::function.
Le funzioni da chiamare sarebbero molto piccole e mi dispiace abbandonare l'inlining.
Per finire mi piace l'uso di usare policy per creare le classi di disegno , in quanto ho moltissime configurazioni ma vorrei poter inserire le classi di disegno (mesh) in un container senza un crollo di prestazioni.
ciao:ciauz:

giuseppe500
20-05-2014, 19:28
La cosa che in assoluto costa meno è passare un functore come parametro template; in tal caso, il compilatore ha il "quadro completo", sa subito chi verrà chiamato e può fare inlining di tutto quel che crede necessario.


grande! era meglio che leggessi con attenzione.
Ho creato un functore base con l'operator()() virtuale ed ho ereditato classi da questo functore, ognuna con una procedura di disegno.
a questo punto con uno shader posso far creare un oggetto ad una classe host, magari passando anche il functore come parametro template ed ho un maggior grado di configurazione shader/funzioni/functori di disegno e questo oggetto è perfettamente inseribile in un container perchè nell oggetto ho inserito un puntatore al functore astratto e vi posso inserire qualsiasi functore di disegno, dato che ereditano tutti dal functore astratto.
grazie.:ciauz:

ps.per lì'inlining come devo fare dato che le funzioni di disegno sono al massimo 20 30 righe di codice , sono troppe? come posso renderle 'inlining? non sono cose che sceglie il compilatore?


ciao.

MItaly
21-05-2014, 12:03
Sì sostanzialmente hai reimplementato quello che fa std::function sotto il cofano. :D
Per l'inlining, essendo che c'è di mezzo una funzione virtuale sostanzialmente non è possibile (o meglio, solo se la chiamata a funzione viene risolta in maniera statica).

non sono cose che sceglie il compilatore?
Certo, difatti la keyword inline sostanzialmente ignorata dai compilatori (che decidono autonomamente se espandere inline o meno), il punto è semplicemente che l'inlining non sempre è possibile (vedi appunto nel caso delle funzioni virtuali).

giuseppe500
22-05-2014, 14:08
Sì sostanzialmente hai reimplementato quello che fa std::function sotto il cofano. :D
Per l'inlining, essendo che c'è di mezzo una funzione virtuale sostanzialmente non è possibile (o meglio, solo se la chiamata a funzione viene risolta in maniera statica).

Certo, difatti la keyword inline sostanzialmente ignorata dai compilatori (che decidono autonomamente se espandere inline o meno), il punto è semplicemente che l'inlining non sempre è possibile (vedi appunto nel caso delle funzioni virtuali).

ho capito molte grazie come sempre.
solo una cosa , io lo faccio al prezzo di una funzione virtuale che è risibile , in confronto all'std::function che non so bene cosa fa ma è molto lento.
ciao.:ciauz:

Loading