2016-06-30 82 views
9

Contesto: Ho deciso di riscrivere il mio script grafico 3D, evoluto in Python, in C per la velocità. Ciò implica l'apprendimento di C. La parte del programma in questione memorizza nella cache le informazioni normali su una mesh 3D.Evitare errori di allocazione (principianti) in questa sequenza di funzioni vettoriali in C

Qui ci sono 3 operazioni vettoriali chiamate in sequenza (piuttosto standard: sottrazione vettoriale per ottenere il vettore del bordo, prodotto incrociato e media), che vorrei essere il più veloce possibile, quindi sto cercando di evitare di archiviare nulla inutilmente sul mucchio ... e copiare le mie strutture troppe volte. Tuttavia, sono anche consapevole del fatto che se restituisco dei puntatori, indicherò lo spazio mem che non è più valido.

In questo modo proverei a scrivere tutte e tre le funzioni (in generale: struct copy in, struct copy out, piuttosto che puntatori).

typedef struct vector vector; 
struct vector{ 
    double x,y,z; 
}; 

vector vect(vector a, vector b){ 
    vector res; 
    res.x = b.x - a.x; 
    res.y = b.y - a.y; 
    res.z = b.z - a.z; 
    return res; 
} 

vector cross(vector a, vector b){ 
    vector res; 
    res.x = a.y*b.z - a.z*b.y; 
    res.y = a.z*b.x - a.x*b.z; 
    res.z = a.x*b.y - a.y*b.x; 
    return res; 
} 

vector avg (vector a, vector b){ 
    vector res; 
    res.x = (a.x + b.x)/2; 
    res.y = (a.y + b.y)/2; 
    res.z = (a.z + b.z)/2; 
    return res; 
} 

Ed ecco come si chiama:

m->Tpolynormals[i] = avg(cross(vect(*p->verts[0], *p->verts[1]), 
           vect(*p->verts[1], *p->verts[2])), 
         cross(vect(*p->verts[2], *p->verts[3]), 
           vect(*p->verts[3], *p->verts[0])) 
         ); 

È questo abbastanza efficiente o c'è un modo più veloce per farlo? So che posso sperimentare e "farlo funzionare", ma a questo punto mi piacerebbe assicurarmi che le basi siano solide. - Grazie

Modifica: aggiunta la mia definizione di struttura sopra, come qualcuno ovviamente ha sottolineato, duh. Le coordinate sono doppie (è ciò che emette il mio pacchetto 3D) e il sistema è a 64 bit.

+3

Non temere i puntatori. Impara semplicemente a usarli correttamente. Per restituire un risultato è sufficiente aggiungere un parametro di destinazione alle funzioni che verranno popolate. Passare e restituire i puntatori è più efficiente del passare e restituire le strutture. –

+0

Perché stai facendo una domanda sull'allocazione (memoria) e poi non pubblichi una singola riga che ha qualcosa a che fare con il presunto argomento? – EOF

+1

@EOF Perché penso di sì. Passare e restituire le strutture da una funzione all'altra è l'utilizzo della memoria quando vengono create copie di esse. Passare puntatori è l'utilizzo della memoria perché devi tracciare dove sta puntando. Se hai un suggerimento per un titolo più accurato, scrivilo. – DailyFrankPeter

risposta

5

"Evitare (principianti) errori di attribuzione" contro "Mi piacerebbe essere il più veloce possibile,"

Quale è più importante?

Se il codice deve essere il più veloce possibile, provare un numero di approcci e un profilo per vedere cosa funziona meglio per voi. Farai degli errori.

La dimensione di vector si trova nella regione di confine per fornire una risposta generale a ciò che è meglio, passando vector in base al valore o al suo indirizzo. Meglio provare entrambi

1) Passare vector in base al valore. OP sembra capirlo bene.

vector vect(vector a, vector b){ 
    vector res; 
    res.x = b.x - a.x; 
    res.y = b.y - a.y; 
    res.z = b.z - a.z; 
    return res; 
} 

2) Passare al numero vector dal relativo indirizzo. Crea posizioni di risultati intermedi. Questa sembra essere la parte OP è incerta.

void V_vect(vector *res, const vector *a, const vector *b){ 
    res->x = b->x - a->x; 
    res->y = b->y - a->y; 
    res->z = b->z - a->z; 
} 

// usage example 
vector res1; 
vector res2; 
V_vect(&res1, p->verts[0], p->verts[1]); 
V_vect(&res2, p->verts[1], p->verts[2]); 
vector res3; 
V_cross(&res3, &res1, &res2); 

V_vect(&res1, p->verts[2], p->verts[3]); 
V_vect(&res2, p->verts[3], p->verts[0]); 
vector res4; 
V_cross(&res4, &res1, &res2); 

V_avg(&m->Tpolynormals[i], &res3, &res4); 

In particolare, segnala ad evitare che la memoria riutilizzo nella stessa chiamata come in seguito. Potrebbe essere un errore da principiante, sia in termini di prestazioni che di codice.

V_cross(&res2, &res1, &res2); 

un modo per velocità cose quando si passa l'indirizzo è quello di utilizzare restrict. Ciò consente a un compilatore di sapere che il codice chiamante sta usando i puntatori alle aree che non si sovrappongono. Questo consente determinate ottimizzazioni del compilatore.

void V_vect(vector * restrict res, const vector * restrict a, const vector * restrict b){ 
    res->x = b->x - a->x; 
    res->y = b->y - a->y; 
    res->z = b->z - a->z; 
} 

Con restrict 1 ° al di sotto chiamata è un comportamento indefinito in quanto si sovrappone ai vector s.

// V_cross(&res2, &res1, &res2); // bad 
V_cross(&res4, &res1, &res2); // good 

provare vari approcci (tra cui @Jonathan Leffler compound literal idea e @Jonathan Leffler inline idea) e utilizza ciò che funziona bene per voi.

+1

Qualche idea su come le dimensioni del codice/velocità del codice che usano i letterali composti C99 e gli inizializzatori designati siano confrontati con le alternative? Ad esempio, 'vector vect (vector a, vector b) {return (vector) {.x = b.x - a.x, .y = b.y - a.y, .z = b.z - a.z}; } '. Gut feel: l'ottimizzatore probabilmente genererà un codice molto simile sia per questo che per il codice pass/return-by-value, quindi non c'è molto, se non nessuno, beneficio alla notazione più compatta ma opaca. –

+0

@ Jonathan Leffler Concorda che la tua idea vale la pena provarla, anche se dubito delle maggiori differenze usando letterali composti. Certamente nessuno dei metodi è di ordine di grandezza più veloce dell'altro. Dipende molto dal compilatore/piattaforma. – chux

+0

Vorrei menzionare un terzo modo - ma lasciare un commento per evitare il disordine. Usa 'vector * vect (vector * res, const vector * a, const vector * b) {... return res; } 'e' avg (& y, cross (& res3, vect (& res1, & v0, & v1), vect (& res2, & v1, & v2), cross (& res6, vect (& res4, & v2, & v3), vect (& res5, & v3, & v0))) 'se si vuole veramente concatenare il codice, – chux

2

Se si desidera ridurre a icona la quantità di dati copiati, è possibile passare i puntatori ai parametri di input e output. Ciò significherebbe comunque che non puoi concatenare le chiamate di funzione come fai sopra, e significa che dovresti avere variabili temporanee per contenere il risultato di ogni chiamata.

Ad esempio:

void vect(vector *a, vector *b, vector *res){ 
    res->x = b->x - a->x; 
    res->y = b->y - a->y; 
    res->z = b->z - a->z; 
} 

// similarly for the other two 

Poi chiamarli:

vector vect1, vect2, vect3, vect4, cross1, cross2; 
vect(p->verts[0], p->verts[1], &vect1); 
vect(p->verts[1], p->verts[2], &vect2); 
vect(p->verts[2], p->verts[3], &vect3); 
vect(p->verts[3], p->verts[0], &vect4); 
cross(&vect1, &vect2, &cross1); 
cross(&vect3, &vect4, &cross2); 
avg(&cross1, &cross2, &m->Tpolynormals[i]);