|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Senior Member
Iscritto dal: Jul 2006
Messaggi: 484
|
[C/C++] Consigli ottimizzazione velocità
Come da oggetto vorrei dei consigli per ottimizzare la velocità del mio programma, principalmente il "collo di bottiglia" del mio programma è la valutazione del seno e del coseno di una certa quantità, (quantità che varia volta per volta ovviamente),funzioni che occupano oltre l'80% del tempo processore. (Ovviamente le funzioni seno e coseno sono richiamate moooooooolte volte
Il programma è già multithreaded, quindi sfrutta più core contemporaneamente. Inoltre se possibile sono graditi dei consigli sull'allocazione e deallocazione rapida di vettori di dimensioni non particolarmente elevate qualche decina di elementi per intenderci, ma ciò apporterebbe solo marginali incrementi prestazionali. Non so se è possibile utilizzare qualche libreria in maniera facile adatta allo scopo, per effettuare le operazioni trigonometriche. SO Windows 7 RC Build 7100 IDE Visual Studio Team System 2010 Beta 1 (mi da un incremento di prestazioni rispetto a Visual Studio 2008 di circa il 20%) Ultima modifica di Rossi88 : 19-07-2009 alle 22:31. |
|
|
|
|
|
#2 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
Sicuro che non puoi usare qualcosaltro?
Per esempio il coseno dell'angolo tra 2 vettori si trova col prodotto scalare, che è decisamente più veloce di qualsiasi funzione trigonometrica. Altro non saprei proprio, senza sapere codice e algoritmo ci vorrebbe la palla di vetro
|
|
|
|
|
|
#3 | |
|
Senior Member
Iscritto dal: Jul 2006
Messaggi: 484
|
Quote:
Codice:
void derivsU(double x ,double y[],double dydx[],double lam)
{
/* La parte iniziale del vettore contiene i dati legati alla parte
* reale dell'ampiezza, mentre la seconda parte contiene la parte
* immaginaria.
*/
int i,j;
int n=N;
double q1,q2,esp;
double K;double Kmod=(1+cos(2*pi/lam*x));
for(i=1;i<=n;i++){
dydx[i]=0;
dydx[i+n]=0;
for(j=1;j<=n;j++){
K=Kmod*intePerParam[i][j];
esp=x*beta[i][j];
q1=K*sin(esp);
q2=K*cos(esp);
dydx[i]-=q1*y[j]+q2*y[j+n];
dydx[i+n]+=q2*y[j]-q1*y[j+n];
}
}
}
La funzione che ho riportato è richiamata anche oltre 3.000.000 di volte (è un dato indicativo), mentre N può assumere valore che spaziano fra qualche decina a 100-200 Ciò che mi lascia perplesso inoltre è che le funzioni che occupano la maggior parte del tempo processore sono sin(esp) e cos(esp) mentre cos(2*pi/lam*x) no, risulta trascurabile (almeno stando all'analizzatore di performance di Visual Studio) Ultima modifica di Rossi88 : 19-07-2009 alle 22:46. |
|
|
|
|
|
|
#4 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
Dunque, mi sembra già ottimizzato a bestia
![]() poi vista la complicatezza dei calcoli che svolgi, scommetto che non puoi scambiare imprecisione per velocità no? Tipo che so, sostituire seno e coseno con una table di valori, calcolare meno valori e poi sfocarli ecc ecc. L'unica cosa da fare sarebbe riorganizzare l'algoritmo in modo di svolgere meno volte la parte critica, comunque non capisco nemmeno che stai facendo (dannati numeri complessi ) quindi non saprei come...Comunque stai facendo un filtro su un'immagine praticamente, quindi ti consiglierei CUDA, se ben usato queste robe se le mangia (a precisione singola però). |
|
|
|
|
|
#5 | |
|
Senior Member
Iscritto dal: Nov 2002
Messaggi: 6287
|
Quote:
io ti consiglio di introdurre nel tuo codice le liberie ACML, utilizzando la funzione vettoriale vrda_sincos. In pratica, raggruppi in un vettore tutti gli elementi su cui vuoi fare seni e coseni e con UNA SOLA chiamata li calcoli tutti di un botto, sfruttando anche le proprietà trigonometriche. Nel tuo caso dei raggruppare i vari esp Link utili: http://developer.amd.com/cpu/Librari...s/default.aspx http://developer.amd.com/cpu/Librari...rda_005fsincos PS: Perchè nei cicli for parti da 1 e non da 0 ? Ultima modifica di Unrue : 19-07-2009 alle 23:31. |
|
|
|
|
|
|
#6 | |
|
Senior Member
Iscritto dal: Jul 2006
Messaggi: 484
|
Quote:
Comunque non si tratta di un filtro su un'immagine, è l'applicazione di una teoria dei campi elettromagnetici. La possibilità di utilizzare una look-up table non credo faccia al caso mio, comunque grazie per il suggerimento |
|
|
|
|
|
|
#7 | |
|
Senior Member
Iscritto dal: Jul 2006
Messaggi: 484
|
Quote:
La domanda dell'indice di partenza è una bella domanda, praticamente il programma mi è stato dato e poi io l'ho ottimizzato (pesantemente, ma togliendo il collo di bottiglia del seno e coseno potrei aumentare la velocità di altre 4-5 volte immagino) e nel programma di partenza tutti gli array sono utilizzati con indice di partenza 1 (sprecando quindi la memoria per il primo elemento) Ultima modifica di Rossi88 : 19-07-2009 alle 23:35. |
|
|
|
|
|
|
#8 | |
|
Senior Member
Iscritto dal: Nov 2002
Messaggi: 6287
|
Quote:
Ultima modifica di Unrue : 19-07-2009 alle 23:38. |
|
|
|
|
|
|
#9 | |
|
Senior Member
Iscritto dal: Jul 2006
Messaggi: 484
|
Quote:
(Grazie comunque del suggerimento vedrò di utilizzare la libreria |
|
|
|
|
|
|
#10 |
|
Senior Member
Iscritto dal: Nov 2002
Messaggi: 6287
|
Le sto utilizzando proprio in questo periodo ed i miglioramenti sono di un buon 25%. Ma tutto dipende dal numero di seni e coseni che devi calcolare. Nel tuo caso ad esempio, se n è molto piccolo, non avrai un grande vantaggio.
Ultima modifica di Unrue : 19-07-2009 alle 23:47. |
|
|
|
|
|
#11 | |
|
Senior Member
Iscritto dal: Jul 2006
Messaggi: 484
|
Quote:
Comunque N varia da qualche decina a 100-200, per molto piccolo su che ordine di grandezza siamo? |
|
|
|
|
|
|
#12 | |
|
Senior Member
Iscritto dal: Nov 2002
Messaggi: 6287
|
Quote:
Un altro mio consiglio è di fare un unrolling del loop più interno con passo 2. Proprio in questi giorni ho a che fare con una funzione chiamata moltissime volte. Un unrolling con passo 2 mi ha fatto raddoppiare la velocità di esecuzione. Esempio: Codice:
for (i = 0; i < 100; i++)
a += vect[i];
//Unroll passo 2:
for (i = 0; i < 100; i += 2)
{
a += vect[i];
a += vect[i+1];
}
Fatto con il passo2, prova con 4, ed 8. Ultimo consiglio, dichiara double dydx[] come restrict ( double* restrict dydx) e metti -restrict nei flags di compilazione. Ultima modifica di Unrue : 20-07-2009 alle 23:59. |
|
|
|
|
|
|
#13 |
|
Senior Member
Iscritto dal: Dec 2005
Messaggi: 558
|
anche io ultimamente mi sono sbattuto parecchio per l'ottimizzazione di qualche programmino numerico in ambito scientifico e ho notato che la miglior ottimizzazione si ottiene solamente utilizzando l'algoritmo migliore, ma immagino che tu sia già arrivato a questo punto
prima di tutto immagino che esp possa avere qualsiasi valore giusto? perchè se (come nel mio caso ad esempio) sai che può avere solo valori molto bassi puoi tranquillamente utilizzare gli sviluppi in serie di sin e cos. Se, come credo, questo non è il tuo caso puoi provare a calcolare solo il sin (o il cos) e poi usare sqrt(1- sin^2), io, prima di rendermi conto che effettivamente lo sviluppo era la cosa migliore da fare, avevo ottenuto un discreto miglioramento (con un test "sintetico" veniva fuori un 30% se non sbaglio). |
|
|
|
|
|
#14 |
|
Senior Member
Iscritto dal: Aug 2005
Messaggi: 579
|
Hai provato a cercare qualche libreria che calcola seno e coseno in tempi più rapidi? Immagino usi la math.h...
Altrimenti l'unica soluzione è rivedere le chiamate perchè chiamare 3000000 di volte una funzione è veramente pesante (e nelle ottimizzazioni si parte sempre dal numero maggiore, dannate derivate parziali)... che so magari con qualche stratagemma riesci a dimezzare, in fin dei conti i calcoli nel campo complesso derivano dalla circonferenza goniometrica che è simmetrica rispetto l'origine quindi in sostanza cambiano i segni ma le "forme" nei 4 pezzetti di piano sono sempre le stesse... |
|
|
|
|
|
#15 |
|
Senior Member
Iscritto dal: Feb 2006
Messaggi: 1304
|
Se beta viene aggiornato meno spesso di quanto tu calcoli derivsU, puoi salvarti il seno e coseno di beta dentro un'altro array (caching), avendo quindi un costo costante per ogni chiamata di derivsU.
Inoltre mi pare ci siano istruzioni vettoriali (SIMD) che calcolano 4 seni e coseni in una sola volta, potrebbero interessarti. |
|
|
|
|
|
#16 |
|
Senior Member
Iscritto dal: Jul 2006
Messaggi: 484
|
Ringrazio tutti delle risposte.
Ho provato ad effettuare l'unroll del ciclo più esterno (un unroll completo, ho fatto giusto una prova su un caso particolare, un unroll di 64) ma le prestazioni non miglioravano (anzi peggioravano leggermente, anche se ciò non ha molto senso) per l'opzione restrict, in visual studio la parola chiave è __restrict ma se la uso da errore, inoltre su MSDN per utilizzare restrict è indicata la modifica da fare al compilatore ma tale opzione non è presente nel mio.... (eppure ho consultato la sezione della versione che utilizzo di Visual studio ) per quanto riguarda la possibilità di utilizzare cos(x)=sqrt(1-sin(x)^2) in questo caso perderei l'informazione legata al segno, oppure dovrei fare degli if, ma a quanto ho letto gli if rallentano l'esecuzione del codice. Per quanto riguarda le librerie, in realtà avevo provato ad utilizzare Intel Compiler, che aveva Math kernel Library, avevo utilizzato su macchina virtuale. Il problema è che non è gratuito il compilatore dell'Intel nè le librerie, sarebbe un po' scocciante ogni tot giorni formattare e reinstallare oppure ricopiare un'immagine della partizione. Si potrebbe pensare di compilare su macchina virtuale ed eseguire sull'host ma il problema è che su macchina virtuale non posso fare l'analisi delle performance (non sono emulate delle cose che non ricordo esattamente). Per quanto riguarda le librerie dell'AMD, le ho scaricate installate, l'istruzione che è riportata sulla guida Codice:
cl driver.c -Ic:/acml4.3.0/win64_mp/include c:/acml4.3.0/win64_mp/lib/libacml_mp_dll.lib Codice:
driver.c c1: fatal error C1083: Cannot open source file: 'driver.c': No such file or directory Ho provato a seguire qualche guida per utilizzare le librerie ma niente da fare Se qualcuno può indicarmi come utilizzare le librerie ACML, o indicarmi qualche link o guida Ultima modifica di Rossi88 : 22-07-2009 alle 14:47. |
|
|
|
|
|
#17 |
|
Senior Member
Iscritto dal: Aug 2005
Messaggi: 579
|
È chiaro che comunque c'è una discrepanza computazionale, la richiesta di 3000000 di chiamate a funzione non è eseguibile a bassa latenza su un normale computer.
O prevedi il fatto che quando si eseguono i calcoli vi è un tempo di attesa oppure non rimane che adottare tecnologie hardware più potenti o addirittura cluster di computer, se le finanze lo permettono. Una domanda: i calcoli devono essere svolti per forza sequenzialmente? Non è possibile dividere quei 3000000 di chiamate su più thread (core)? |
|
|
|
|
|
#18 | ||||
|
Senior Member
Iscritto dal: Nov 2002
Messaggi: 6287
|
Quote:
Quote:
Quote:
Quote:
Ultima modifica di Unrue : 22-07-2009 alle 22:02. |
||||
|
|
|
|
|
#19 | |
|
Senior Member
Iscritto dal: Jul 2006
Messaggi: 484
|
Quote:
Ultima modifica di Rossi88 : 23-07-2009 alle 12:07. |
|
|
|
|
|
|
#20 | |
|
Senior Member
Iscritto dal: Aug 2005
Messaggi: 579
|
Quote:
Se usi angoli entro una certa approssimazione puoi ad esempio tabularti... a costo di usare un buon 5-6MB di memoria però hai un accesso a costo costante senza calcoli, meglio di così non saprei proprio. |
|
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 16:26.




















