Dopo aver provato un po 'di cose sulla carta, ho trovato qualcosa che potrebbe funzionare per voi. È un'implementazione perfettamente parallelizzata/vettoriale della funzione in SSE.
Tuttavia richiede la riorganizzazione dei dati perché eseguiremo l'elaborazione parallela per 4 triangoli contemporaneamente.
Scomporlo in passaggi e utilizzare i nomi delle istruzioni qua e là, ma si prega di utilizzare i C intrinsechi (_mm_load_ps(), _mm_sub_ps() et al, sono in xmmintrin.h in VC) - quando io parlare di registri significa solo __m128.
FASE 1.
Non abbiamo bisogno della coordinata Y del tutto, così abbiamo istituito puntatori a coppie di X e Z. fornire almeno 4 paia (cioè 4 triangoli Totale) per chiamata. Chiamerò ogni coppia X e Z un vertice.
FASE 2.
Uso MOVAPS (richiede i puntatori da allineare a 16-bit) per caricare i primi due vertici puntati da ogni puntatore in registri.
Un registro caricato da un sarà simile a questa: [a0.x, a0.z, a1.x, a1.z]
FASE 3.
Ora, utilizzando una singola istruzione sottrarre, è possibile calcolare i delta (il vostro V0, v1, v2) per 2 vertici in una sola volta.
Calcolare V0, v1 e v2 non solo per i primi 2 triangoli, ma anche per questi ultimi 2! Come ho detto dovresti fornire un totale di 4 vertici, o 8 galleggianti, per input. Solo ripetere i passaggi 2 e 3 per i dati.
Ora abbiamo 2 coppie di registri vx, ciascuna coppia contenente il risultato per 2 triangoli. Mi riferirò a loro come vx_0 (prima coppia) e vx_1 (seconda coppia) in cui x va da 0 a 2.
punto 4.
Dot prodotti. Per parallelizzare il calcolo baricentrico (in seguito) è necessario il risultato di ciascun prodotto punto per ciascuno dei 4 triangoli, in 1 registro singolo.
Quindi, dove calcolereste dot01 ad esempio, faremo lo stesso, ma per 4 triangoli in una volta. Ogni v -registrazione contiene il risultato per 2 vettori, quindi iniziamo moltiplicandoli.
Diciamo che u e v - parametri nella vostra funzione prodotto scalare - ora sono v0_0 e v1_0 (da calcolare dot01):
Multiply u e v per ottenere: [(v0_0.x0) * (v1_0.x0), (v0_0.z0) * (v1_0.z0), (v0_0.x1) * (v1_0.x1), (v0_0.z1) * (v1_0.z1)]
Questo può sembrare confuso a causa della .x0/.z0 e .x1/.z1, ma guardare a ciò che è stato caricato al punto 2: a0, a1.
Se ormai questo si sente ancora confuso, prendi un pezzo di carta e scrivi dall'inizio.
successivo, sempre per lo stesso prodotto scalare, fare la moltiplicazione per v0_1 e v1_1 (cioè la seconda coppia di triangoli).
Ora abbiamo 2 registri con 2 coppie X & Z (4 vertici totali), moltiplicati e pronti per essere aggiunti insieme per formare 4 punti separati.SSE3 ha un'istruzione per fare esattamente questo, e si chiama HADDPS:
xmm0 = [A, B, C, D] XMM1 = [E, F, G, H]
HADDPS xmm0, XMM1 fa questo:
xmm0 = [A + B, C + D, E + F, G + H]
Ci vorranno le X & coppie Z dei primo registro, quelle della seconda, aggiungere insieme e memorizzarli nel primo, secondo, terzo e quarto componente del registro di destinazione. Ergo: a questo punto abbiamo questo particolare prodotto dot per tutti e 4 i triangoli!
** Ora ripetere questo processo per tutti i prodotti dot: dot00 eccetera. **
STEP 5.
L'ultimo calcolo (per quanto ho potuto vedere dal codice in dotazione) è la roba baricentrica. Questo è un calcolo scalare al 100% nel codice. Ma i tuoi input ora non sono risultati di prodotti a punti scalari (cioè singoli float), sono vettori/registri SSE con un prodotto dot per ciascuno dei 4 triangoli.
Quindi, se si esegue il flatout di questa operazione utilizzando le operazioni SSE parallele che operano su tutti e 4 i float, alla fine si otterrà 1 registro (o risultato) che trasporta 4 altezze, 1 per ciascun triangolo.
Dal momento che la mia pausa pranzo è scaduta, non ho intenzione di scrivere questo, ma dato il setup/idea che ho dato questo è un ultimo passo e non dovrebbe essere difficile da capire.
So che questa idea è un po 'lunga e richiede un po' di amore dal codice che si trova sopra di esso e forse un po 'di tempo con carta e matita, ma sarà veloce (e puoi anche aggiungere OpenMP in seguito se ti piacerebbe).
Buona fortuna :)
(e perdona la mia spiegazione fuzzy, posso montare la funzione se necessario =))
UPDATE
ho scritto un'implementazione ed non è andato come mi aspettavo, principalmente perché la componente Y è stata coinvolta oltre il pezzo di codice che hai inizialmente incollato nella tua domanda (l'ho cercato). Quello che ho fatto qui non è solo chiedervi di riorganizzare tutti i punti in coppie XZ e dar loro da mangiare per 4, ma anche di alimentare 3 puntatori (per i punti A, B e C) con i valori Y per ciascuno dei 4 triangoli. Da una prospettiva locale questo è il più veloce. Posso ancora modificarlo per richiedere modifiche meno invadenti dalla fine del callee, ma per favore fammi sapere cosa è desiderabile.
Quindi una dichiarazione di non responsabilità: questo codice è semplice come l'inferno (qualcosa che ho trovato che funziona molto bene con i compilatori in termini di SSE ... possono riorganizzarsi come si vede e le CPU x86/x64 prendono la loro parte anche in esso). Anche la denominazione, non è il mio stile, non è di nessuno, fallo con quello che ritieni opportuno.
speriamo vi sia utile e se non sarò contento di andare su di esso di nuovo.E se questo è un progetto commerciale c'è anche la possibilità di me di salire a bordo immagino;)
In ogni caso, ho messo su pastebin: http://pastebin.com/20u8fMEb
Dubito che questa funzione foglia possa essere ulteriormente ottimizzata perché fa solo 3 operazioni FP. Forse nei siti di chiamata è possibile un'ottimizzazione. – hirschhornsalz
Se questa funzione viene chiamata molto, provare ad usare OpenMP se ha senso. – jn1kk
IMO che non è l'approccio giusto. SSE non è realmente pensato per operazioni orizzontali. Ci sono alcune istruzioni orizzontali, ma sono (quasi) tutte lente. Con SSE è quasi sempre meglio calcolare 4 di qualcosa contemporaneamente anziché provare a fare 1 cosa 4 volte più velocemente. – harold