|
|||||||
|
|
|
![]() |
|
|
Strumenti |
|
|
#1 |
|
Senior Member
Iscritto dal: Sep 2006
Città: Bologna/Milano
Messaggi: 525
|
[c++ vs matlab] ottimizzare codice c++ per configgere matlab
Ciao a tutti!
spero che l'argomento possa essere di interesse a qualcuno visto che per la mia testolina da programmatore della domenica non è banale :zizi: allora sto scrivendo un software che attraverso simulazione di montecarlo trovi il prezzo di un certo derivato esotico (precisamente una opzione magrabe ma non credo vi sia di interesse) l'ho fatto sia in matlab che in c++, sicuro che il c++ avrebbe stracciato matlab di tanto... e invece mi ritrovo con un codice matlab piu veloce eccovi le due funzioni incriminate c++ Codice:
//using normal distribution object from Technical Report 1
typedef std::tr1::ranlux64_base_01 Myeng;
typedef std::tr1::normal_distribution<double> ndistr;
double Margrabe_option_MC_price(double r, double t, double s1, double s2, double q1, double q2,
double std1, double std2, double corr, double iterations){
//variables and objects initialization
register long x;
double price = 0; //the final price will be stored here
double payoff;
double z1, z2; //to store generated random numbers
double matr21, matr22;
double sigma1, sigma2;
double part1, part2, sqrtt;
clock_t start, stop;
Myeng eng;
ndistr stdnorm(0, 1); //creating the standard normal distribution generator
//seeding the random number generator
eng.seed((unsigned long) time(NULL));
//calculating sigmas
matr21 = corr * std2;
matr22 = sqrt(1-corr*corr) * std2;
//precalculating fixed part of drive formula
part1 = exp((r - q1 - 0.5*std1*std1)*t);
part2 = exp((r - q2 - 0.5*std2*std2)*t);
sqrtt = sqrt(t);
for(x=0; x < iterations ;x++){
//generating random numbers
z1 = stdnorm(eng);
z2 = stdnorm(eng);
//creating sigmas
sigma1 = z1 * std1;
sigma2 = matr21 * z1 + matr22 * z2;
//calculating payoff
payoff = (s1 * part1 * exp(sqrtt * sigma1)) - (s2 * part2 * exp(sqrtt * sigma2));
price += (payoff > 0) ? payoff : 0;
}
price /= iterations;
price *= exp((-r)*t);
return price;
}
Codice:
function [p,ci] = ExchangeMC(V0,U0,sigmaV,sigmaU,rho,T,r,NRepl) eps1 = randn(1,NRepl); eps2 = rho*eps1 + sqrt(1-rho^2)*randn(1,NRepl); VT = V0*exp((r - 0.5*sigmaV^2)*T + sigmaV*sqrt(T)*eps1); UT = U0*exp((r - 0.5*sigmaU^2)*T + sigmaU*sqrt(T)*eps2); DiscPayoff = exp(-r*T)*max(VT-UT, 0); [p,s,ci] = normfit(DiscPayoff); Considerando che ho sempre pensato il c/c++ essere i linguaggi di alto livello piu performanti che esistono (correggetemi se sbaglio) devo aver sbagliato io qualcosa. come rendereste voi il codice c piu efficiente? da ignorante mi è venuto da pensare che il problema potrebbe essere nella funziona di generazione di numeri casuali di una normale standard!! ho controllato e matlab lo risolve con questi calcoli eps1 = randn(1,NRepl); eps2 = rho*eps1 + sqrt(1-rho^2)*randn(1,NRepl); in un elegante 8 centesimi mentre la funzione che ho trovato io nelle librerie del boost che da quel che ho capito riscreano il pacchetto aggiuntivo Technical Report 1 for(x=0; x < iterations ;x++){ //generating random numbers z1 = stdnorm(eng); z2 = stdnorm(eng); } se ne esce con un goffo 26 centesimi di secondo come posso migliorare questo aspetto? inoltre avete altre osservazioni sui codici?? ci tengo particolarmente a rendere il c++ piu performante... grazie Marco
__________________
http://mamo139.altervista.org |
|
|
|
|
|
#2 |
|
Senior Member
Iscritto dal: Jul 2005
Città: Bologna
Messaggi: 1130
|
Non sono un gran esperto di numeri casuali, ma hai già provato anche con altri engine?
__________________
-> The Motherfucking Manifesto For Programming, Motherfuckers |
|
|
|
|
|
#3 |
|
Senior Member
Iscritto dal: Jul 2011
Messaggi: 381
|
ciao, come suppongo tu abbia un pc multicore, potresti cercare di adottare una soluzione di calcolo parallelo multithreading.
Per esempio nel tuo codice il calcolo di z1 e sigma1 può essere messo in parallelo con il calcolo di z2. Più parallelizzi e più hai possibilità che il programma vada più veloce. (Ho detto possibilità in quanto 10 thread su un dual core non vanno quasi sicuramente meglio di 2 thread
__________________
Concluso positivamente con: Kamzata, Ducati82, Arus, TheLastRemnant, ghost driver, alexbull1, DanieleRC5, XatiX |
|
|
|
|
|
#4 | ||
|
Senior Member
Iscritto dal: Sep 2006
Città: Bologna/Milano
Messaggi: 525
|
Quote:
qualcuno conosce qualche motore particolarmente efficiente? ho il sospetto che matlab sia efficiente nel generare n numeri casuali contemporaneamente, piuttosto che n volte singolarmente Quote:
ma questa sarà l'ultima chicca, voglio almeno pareggiare prima di andare al multithreading
__________________
http://mamo139.altervista.org Ultima modifica di mamo139 : 16-11-2011 alle 11:53. |
||
|
|
|
|
|
#5 | |
|
Senior Member
Iscritto dal: Jul 2005
Città: Bologna
Messaggi: 1130
|
Quote:
__________________
-> The Motherfucking Manifesto For Programming, Motherfuckers |
|
|
|
|
|
|
#6 |
|
Senior Member
Iscritto dal: Jul 2011
Messaggi: 381
|
se matlab usa il multithreading sarà molto difficile andar più veloce con un single thread... non credo che il C++ basti, potresti provare soluzioni asm miste c++
__________________
Concluso positivamente con: Kamzata, Ducati82, Arus, TheLastRemnant, ghost driver, alexbull1, DanieleRC5, XatiX |
|
|
|
|
|
#7 | |
|
Senior Member
Iscritto dal: Sep 2006
Città: Bologna/Milano
Messaggi: 525
|
DUE MIGLIORIE:
ieri notte ho separato la generazione dei numeri random immagazzinandoli in un array, quindi ora ci sono due for al posto di uno ma c'è stata una miglioria: da 0.44 a 0.34 questa mattina ho sostituito il tr1 con: una versione del Mersenne Twister per generare numeri random e una funzione che pare essere molto ottimizzata per normalizzarli (spero sia fatta abbastanza bene da non crearmi problemi a livello di qualità dei dati )per esequire 10 simulazioni da 1.000.000 ci mette ora 0.30 al posto di 0.34 del tr1 ma bisogna migliorare ancora eccovi il codice allo stato attuale: Codice:
inline double Margrabe_option_MC_price(double r, double t, double s1, double s2, double q1, double q2,
double std1, double std2, double corr, long iterations){
//variables and objects initialization
register long x,y;
double price = 0; //the final price will be stored here
double payoff;
double *z1; //to store generated random numbers
double matr21, matr22;
double sigma1, sigma2;
double part1, part2, sqrtt;
//memory allocation
z1 = new double[iterations*2];
//seeding the random number generator
#ifdef RANDOM_MODE_TR1
Myeng eng;
ndistr stdnorm(0, 1); //creating the standard normal distribution generator
eng.seed((unsigned long) time(NULL));
#endif
#ifdef RANDOM_MODE_Mersenne_twister
init_genrand((int)time(NULL));
#endif
//calculating sigmas
matr21 = corr * std2;
matr22 = sqrt(1-corr*corr) * std2;
//precalculating fixed part of drive formula
part1 = exp((r - q1 - 0.5*std1*std1)*t);
part2 = exp((r - q2 - 0.5*std2*std2)*t);
sqrtt = sqrt(t);
for(x=0; x < iterations ;x++){
y=x+x;
//generating random numbers
#ifdef RANDOM_MODE_TR1
z1[y] = stdnorm(eng);
z1[y+1] = stdnorm(eng);
#endif
#ifdef RANDOM_MODE_Mersenne_twister
z1[y] = normsinv(genrand_real3());
z1[y+1] = normsinv(genrand_real3());
#endif
}
for(x=0; x < iterations ;x++){
y=x+x;
//creating sigmas
sigma1 = z1[y] * std1;
sigma2 = matr21 * z1[y] + matr22 * z1[y+1];
//calculating payoff
payoff = (s1 * part1 * exp(sqrtt * sigma1)) - (s2 * part2 * exp(sqrtt * sigma2));
if(payoff > 0) price += payoff;
}
price /= iterations;
price *= exp((-r)*t);
delete [] z1;
return price;
}
Quote:
io purtroppo asm non lo conosco, ma se tu hai voglia di farmi vedere come potrei sostituire qualche pezzetto con codice asm apprezzerei molto (e poi così mi metto anche a vedere come funziona l'assembly)
__________________
http://mamo139.altervista.org |
|
|
|
|
|
|
#8 |
|
Senior Member
Iscritto dal: Jul 2011
Messaggi: 381
|
ciao, dunque per prima cosa devi definire una funzione, per esempio creo una funzione che faccia y=x+x;
la chiamo somma che dovrà fare queste semplici cose int somma(int x){ int y; y=x+x; return y; } Per prima cosa, per tradurra in asm bisogna fare la traduzione dei nomi; poiché tu usi C++ per comodità (anche se si potrebbe fare a meno) bisogna dichiararla extern quindi avremo extern "C" somma(int x) A questo punto passiamo all'asm e quindi al file .s che noi chiameremo fun.s quindi Codice:
.text .global _somma _somma: pushl %ebp movl %esp, %ebp subl $4, %esp movl 8(%ebp), %eax addl 8(%ebp), %eax leave ret Ora, niente panico e facciamo chiarezza. Per prima cosa di semplice intuizione il .global serve per il nome e la successiva istruzione definisce la funzione. Poi, salvo il contenuto del registro ebp, esso va salvato perché non vogliamo che il suo contenuto si perda. Successivamente sposto l'indirizzo base del record di attivazione che è in esp in ebp (dato che non vogliamo perdere l'indirizzo della pila che mi serve per "trovare" le variabili passate alla funzione). A questo punto creo spazio per le variabili locali ovvero un int cioè 32 bit cioè 4 byte e quindi sposto il puntatore della pila di una posizione in "su" appunto per creare lo spazio per la variabile y. A questo punto il mio record di attivazione è d (valore) -4 <- punto si trova l'indirizzo EBP EIP (+4) x (valore che è stato passato alla funzione) (+8) Quindi per prendere il valore x devo "scendere" nel mio record a +8 (ricordati sempre che c'è sempre EIP che occupa 4byte!!!) quindi ecco spiegato la movl 8(%ebp), %eax ovvero "sposta il CONTENUTO della locazione di memoria identificato dall'INDIRIZZO contenuto in ebp incrementato di 8 byte in EAX". Successivamente faccio banalmente la somma e con leave elimino lo spazio delle variabili locali ripristinando il valore di esp e successivamente di ebp. Infatti il codice della leave equivale a Codice:
movl %ebp, %esp popl %ebp
__________________
Concluso positivamente con: Kamzata, Ducati82, Arus, TheLastRemnant, ghost driver, alexbull1, DanieleRC5, XatiX |
|
|
|
|
|
#9 |
|
Senior Member
Iscritto dal: May 2005
Città: Trieste
Messaggi: 2285
|
probabilmente nel tuo caso è trascurabile ma io eviterei nei cicli for il post incremento(i++) a favore del preincremento(++i)
questo perchè il post crea un temp con il valore dell'indice originale, incrementa l'indice e ritorna il temp: hai quindi una variabile in più inutilizzata per ogni iterazione inoltre in alcuni casi può essere utile procedere all'unroll dei cicli for, ma non so se questo è il caso (si può sempre tentare
__________________
neo mini v2 / asus strix z490i / 10600k@? / uh12s / rx6700xt / 32gb ddr4@3200 / sandisk 250 + asenno 1tb / lenovo g34w
trattative concluse : tante... |
|
|
|
|
|
#10 |
|
Senior Member
Iscritto dal: Dec 2005
Città: Istanbul
Messaggi: 1817
|
ancora prima direi che vale la pena di dare una profilata per vedere dove viene speso il tmepo, senza tentare ottimizzazioni a casaccio
__________________
One of the conclusions that we reached was that the "object" need not be a primitive notion in a programming language; one can build objects and their behaviour from little more than assignable value cells and good old lambda expressions. —Guy Steele |
|
|
|
|
|
#11 |
|
Senior Member
Iscritto dal: May 2001
Messaggi: 12904
|
Probabilmente Matlab avrà alcune parti scritte in assembly e ottimizzate per l'uso di alcune estensioni dei processori moderni per il calcolo vettoriale.
|
|
|
|
|
|
#12 |
|
Senior Member
Iscritto dal: Jan 2001
Città: Livorno
Messaggi: 1378
|
Se scopri che la maggior parte del tempo viene spesa nella generazione dei numeri casuali prova ad usare una pennina come questa questo:
![]() http://www.entropykey.co.uk/ Hai anche il vantaggio di avere numeri realmente casuali e non pseudocasuali come quelli ottenibili con metodi software. Nel sito dice che incrementa moltissimo le prestazioni rispetto agli algoritmi software normalmente utilizzati. |
|
|
|
|
|
#13 | |
|
Senior Member
Iscritto dal: Oct 2004
Messaggi: 1945
|
Quote:
|
|
|
|
|
|
|
#14 | |
|
Senior Member
Iscritto dal: May 2005
Città: Trieste
Messaggi: 2285
|
Quote:
quindi mamo139 profila(vs2010, non ricordo se professional o ultimate, ha un discreto tool di profilazione integrato) e poi si vedrà dove mettere mano
__________________
neo mini v2 / asus strix z490i / 10600k@? / uh12s / rx6700xt / 32gb ddr4@3200 / sandisk 250 + asenno 1tb / lenovo g34w
trattative concluse : tante... |
|
|
|
|
|
|
#15 |
|
Senior Member
Iscritto dal: Oct 2004
Messaggi: 1945
|
Scusami ma perchè hai questa cosa
Codice:
for(x=0; x < iterations ;x++){
y=x+x;
//generating random numbers
#ifdef RANDOM_MODE_TR1
z1[y] = stdnorm(eng);
z1[y+1] = stdnorm(eng);
#endif
#ifdef RANDOM_MODE_Mersenne_twister
z1[y] = normsinv(genrand_real3());
z1[y+1] = normsinv(genrand_real3());
#endif
}
for(x=0; x < iterations ;x++){
y=x+x;
//creating sigmas
sigma1 = z1[y] * std1;
sigma2 = matr21 * z1[y] + matr22 * z1[y+1];
//calculating payoff
payoff = (s1 * part1 * exp(sqrtt * sigma1)) - (s2 * part2 * exp(sqrtt * sigma2));
if(payoff > 0) price += payoff;
}
|
|
|
|
|
|
#16 | |
|
Senior Member
Iscritto dal: Sep 2006
Città: Bologna/Milano
Messaggi: 525
|
alla fine ho risolto il problema e battuto matlab ottimizzato per multithreading (utilizzato su un dual core) con un codice C non ancora ottimizzato
ho trovato un super algoritmo di generazione e l'ho implementato! in questo caso matlab ha un ottimo algoritmo di generazione di numeri casuali!! ecco l'algoritmo utilizzato per chiunque fosse interessato http://www.mathworks.it/company/news...g01_cleve.html avrò provato una decina di algoritmi trovati su dei paper prima di arrivare a questo e nessuno andava così bene! quindi se vi interessa l'argomento consiglio la lettura! ora ottimizzo anche il mio codice C per andare in multithreading e vi posto i risultati e il codice finale! ------------------------------------------------------------------------------------ figa la chiavetta generatrice ma non ho fondi ne tempo per provarla Quote:
una profilazione un po casereccia sul sito ufficiale di matlab. l'ho anche verificata avviando matlab in modalità senza multithreading e ho verificato che le funzioni di operazioni fra vettori traggono grandissimi benefici dal multithreading invece ad esempio rand no perchè non è ottimizzata (c'era scritto anche questo sul sito ufficiale, ma ho verificato ed è vero) scusami l'ignoranza, cosa intendi per unroll dei cicli for?
__________________
http://mamo139.altervista.org Ultima modifica di mamo139 : 20-11-2011 alle 16:50. |
|
|
|
|
|
|
#17 | |
|
Senior Member
Iscritto dal: Dec 2005
Messaggi: 558
|
Quote:
Probabilmente perderai parecchio tempo a fare esponenziali. Compili con -ffast-math? Se gli argomenti degli exp hanno un limite minore e maggiore puoi ottimizzare con una look up table. Ma prima ti conviene capire dov'è che il tuo programma passa la maggior parte del tempo. |
|
|
|
|
|
|
#18 | |||
|
Senior Member
Iscritto dal: Sep 2006
Città: Bologna/Milano
Messaggi: 525
|
Quote:
comunque matlab implementa il multithreading in automatico per la maggior parte delle funzioni base su matrici. Se si vuole disabilitare il multithreading bisogna farlo all'avvio come opzione. ma tanto lo batto con un core solo Quote:
Quote:
__________________
http://mamo139.altervista.org |
|||
|
|
|
|
| Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 00:40.












)








