Verfasst am: 01.12.2011, 19:40
Titel: Matlab Monte Carlo Code Optimieren
Hallo liebe Leute,
ich bin ein Matlab Anfänger, weiß aucher auch, dass Matlab schleifen nur sehr langsam abarbeitet. Viel effizienter wäre eine vektorielle Lösung, kann mir aber leider keine für mein Problem vorstellen.
Code:
function[] = MC(N,sigma)
S0=1000;
T=5; %Zeit in Jahren
n=356; %Steps / Jahr
dt=1/n;
r=1.05;
k=2700;
dlh=zeros(1,T*n);
Durchschnitt=0;
alphaa=0;
for i=1:N
for j=1:T*n
if(j==1) dlh(1,j)=S0*exp((r/n-sigma^2/2)*dt+sigma*sqrt(dt)*randn());
else dlh(1,j)=dlh(1,j-1)*exp((r/n-0.5*sigma^2)*dt+sigma*sqrt(dt)*randn());
end end
i
f(i)=sum(dlh)/(T*n);
alphaa=alphaa+f(i);
end
alphaa=alphaa/N
for k=1:N
dlhh(k)=(f(k)-alphaa)^2;
end
sc=sqrt(sum(dlhh)/(N-1)) end
Das sollte eine Monte Carlo Simulation einer asiatischen Option sein. DH ich brauch die Aktienkurse die vom vorhergehenden abhängen, sowohl die Mittelwerte.
Da ich aber bis zu 10 Mio Simulationen durchführen soll, dauert das schon eine ganze Weile.
Ich hoffe ihr könnt mir helfen, da ich diese Simulation einigemale durchführen sollte und ich nicht jedesmal Stunden warten möchte.
Verfasst am: 01.12.2011, 23:00
Titel: Re: Matlab Monte Carlo Code Optimieren
Hallo knuck1e$_,
Es stimmt, dass Matlab bei Schleifen sehr langsam ist - für die Matlab-Versionen vor 6.5 (Juli 2002). Seitdem sorgt die JIT-Acceleration für eine ordentliche Performance. Wenn die Vektorisierung dazu führt, das optimierte BLAS-Funktionen verwendet werden können, kann eine deutlich schnellere Ausführung bedeuten - muss aber nicht. Falls die Vektorisierung nämlich große temporäre Arrays benötigt, ist der schöne Vorteil schnell dahin, da der Zugriff auf den Speicher weitaus länger dauert als eine kleine Schleife, die vollständig in den Prozessor-Cache passt.
Wenn ich Deinen Code im Editor anschaue, meldet MLint schon einige Probleme. Hast Du eine moderne Matlab-Version? Zumindest seit R2006a sollte man unbedingt auf die MLint-Warnungen hören, wenn man effizienten Code schreiben möchte.
"k=2700;" Löschen, es wird nicht benutzt.
"Durchschnitt=0;" Löschen, ungenutzt.
Ein BEfehl pro Zeile und die JIT-Compilation nicht zu behindern.
Keine Ausgaben ins Command-Window, da dies viel Zeit kostet.
Pre-allocation! "dlhh" in jeder Zeile wachsen zu lassen erzeugt eine exponentiell Laufzeitverlängerung - sehr tückisch.
Du rechnest in jeder Iteration SQRT(dt) aus - einmal reicht. Man kann aber auch gleich das ganze Argument der EXP-Funktion als Konstanten formulieren.
Eine FOR-Schleifen und die erste Iteration per IF umleiten ist teuer. Es ist schneller, den Fall j==1 explizit zu formulieren und die Schleife ab 2 laufen zu lassen.
Ergebnis:
Code:
function sc = MC(N, sigma)
S0 = 1000;
T = 5; % Zeit in Jahren
n = 356; % Steps / Jahr
dt = 1/n;
r = 1.05;
Tn = T * n; % Once only!
f = zeros(1, N);
for i = 1:N
a = S0 * exp(C2 + C1 * randn);
S = 0;
for j = 2:Tn
b = a * exp(C2 + C1 * randn);
S = S + b;
a = b;
end
f(i) = S / Tn;
end
alphaa = sum(f);
dlhh = f - alphaa / N;
sc = sqrt(sum(dlhh .* dlhh) / (N - 1));
end
So komme ich bei 10 Wiederholungen for MC(1000, 4) von 7.0 auf 2.1 Sekunden.
Das vektorisieren ist übrigens nicht trivial, da jedes Element der Summe sich auf sein vorhergehendes bezieht. Eine C-Mex-Funktion wäre sinnvoller als eine Vektorisierung.
Gruß, Jan
PS. Bist Du sicher, dass das Jahr 356 Steps enthält?
Bitte sicherheitshalber überprüfen, ob der Code noch dasselbe macht
Ob Vektorisierung hier sinnvoll ist und in welchem Maße, wird von der Anzahl der Simulationen, der MATLAB-Version und letztlich auch dem Rechner abhängen.
1 Mio. Simulationen könnten z.B. in der Version MC3 den Speicher sprengen Da würde ich dann evtl. auf eine Mischung aus vektorisierter und "normaler" Version zurückgreifen.
Dies benötigt 1.20 Sekunden. (Mit S0 innerhalb der Summe übrigens auch 1.36 Sekunden).
Prima Zeit, Harald!
Bei MC(100000,80) stieg Matlab bei der voll-vektoriesierten (kleiner Scherz) Fassung wegen Speicherproblemen aus (1.3GB freies RAM), während meine gereinigte FOR-Methode immer noch fast die doppelte Zeit Deiner halb-vektorisierten Methde benötigt.
Aber man sieht noch etwas anderes: Das Bereinigen und Vereinfachen des Codes sparte 70% Laufzeit und machte den Code so übersichtlich, dass die Vektorisierung deutlich einfacher wurde. Die teil-vektorisierte Fassung sparte 85% der Laufzeit und die voll-vektorisierte 86%.
Das Vereinfachen macht also zunächst den größten Anteil aus, hat aber auch noch den Vorteil, dass das Debuggen leichter fällt. Zudem lassen sich die Methoden zur Simplifizierung auf alle anderen Programmiersprachen (und auch den Rest des Lebens...) anwenden.
Gruß, Jan
knuck1e$_
Gast
Beiträge: ---
Anmeldedatum: ---
Wohnort: ---
Version: ---
Verfasst am: 02.12.2011, 10:22
Titel:
Vielen dank für die große Beteiligung und die Mithilfe!
Entschuldigung auf das hab ich vergessen. Habs mit google probiert und keine Treffer bekommen.
War im nachhinein wohl auch zu unkonkret.
Habs jetzt nochmal laufen lassen und eigentlich keine merkliche Geschwindgkeitsgewinne feststellen können. Meine Prozessorleistung ist bei 50%, trotz der Einstellung Echtzeit und die Laufvariable steigt "gemächlich". Die 10 Millionen Simulationen die mir mein Bachelorbetreuer vorgeschrieben hat, würde so sicher einige Stunden in Anspruch nehmen. Bin ich an den Grenzen von Matlab oder nur an den Grenzen meiner Intelligenz?
hier nochmal der Code und danke für eure liebe Mithilfe!
Code:
function sc = MC2(N, sigma)
S0 = 1000;
T = 5; % Zeit in Jahren
n = 356; % Steps / Jahr
dt = 1/n;
r = 1.05;
Tn = T * n; % Once only!
C1 = sigma * sqrt(dt);
C2 = (r / n - 0.5 * sigma * sigma) * dt;
for i = 1:N
i
S = S0 * sum(cumprod(exp(C2 + C1*randn(1, Tn))));
f(i) = S / Tn;
end
alphaa = sum(f)/N
dlhh = f - alphaa;
sc = sqrt(sum(dlhh .* dlhh) / (N - 1));
end
Es hilft natürlich immer, einen ordentlichen Rechner zu haben. Mein lieber Laptop braucht für 1e5 Simulationen gerade mal 6,5 Sekunden. Auf 1e7 Simulationen wären das rund 10 Minuten, also im Vergleich zur Dauer einer Bachelor-Arbeit recht erträglich.
Mit der Anzeige von i in jeder Iteration braucht der Code bei mir rund doppelt so lang; das würde ich also definitiv lassen. Wenn man einen Fortschrittsbalken will, bietet sich WAITBAR an. Aber Achtung: auch der waitbar sollte nicht in jeder Iteration aktualisiert werden, da das sonst länger dauert als die Rechnung an sich.
Mit dieser kleinen Änderung (MATLAB-Pool öffnen, for --> parfor) komme ich für die Programmlaufzeit auf 2,7 Sekunden.
Für deine Arbeit müssen alleine 3,56*10^12 (!!) Zufallszahlen erzeugt werden, und gearbeitet werden muss natürlich auch noch damit. Das geht also einfach nicht im Millisekundenbereich.
Grüße,
Harald
P.S.: Nochmal die Nachfrage: willst du wirklich mit 356 Tagen rechnen? Das Jahr hat nunmal 365, und wenn du nur mit Geschäftstagen rechnest, wären wir eher bei 256 als 356.
ich habe mal diesen alten Thread hochgeholt da es bei mir ebenso um eine MC-Simulation geht und ich Unterstützung in Sachen Laufzeit brauche Bei der erforderlichen Anzahl an Simulationsläufen für meinen gesamten Datensatz lande ich momentan grob überschlagen bei 22 Tagen Rechenzeit
Meine Simulation soll Folgendes machen:
-Mittels Rolling-Window die Kovarianzmatrix der Aktien ermitteln
-Tageweise multivariate Zufallszahlen mit mu = 0 und sigma = kovarianzmatrix erzeugen
-prüfen ob die generierte Zufallszahl am entsprechenden Tag und für das entsprechende Unternehmen kleiner-gleich oder größer als die zugehörige PD (Ausfallswahrscheinlichkeit) ist
- wenn kleiner-gleich soll sie an der entsprechenden Stelle eine 1 für "Ausfall des Unternehmens" eintragen, andernfalls eine 0
-anschließend die Ausfallsumme berechnen pro Tag, und nach dem Größenanteil des Unternehmens am Gesamtwert der Unternehmen gewichten
-die Ergebnisse eines Tages addieren und als "F" in das Hauptfile zurückgeben Speicherung pro Durchlauf zwecks Mittelwertbildung erfolgt im Hauptfile
Code:
function F = MC1test(AR, window,PDs,AVs,alldates,numbanks, LGD)
zufallsMat = zeros(numbanks, 1);
F = zeros(1, numel(alldates)-window);
covMat = NaN(numbanks);
for i = 1: numel(alldates)-window
%Asset-Returns pro Durchlauf des Rolling Window einlesen
X = AR(i:i+window,1:numbanks);
%PD und Asset-Value für den Tag nach dem Rolling Window
PD = PDs(i+window, 1:numbanks)';
AV = AVs(i+window, 1:numbanks)';
%Gewichtung nach Assetwert-Anteil am Gesamtwert
total = nansum(AV);
weigth = AV/total;
X(isnan(X)) = [0];
covMat = cov(X);
MU = zeros(1, length(covMat));
SIGMA = covMat;
zufallsMat = mvnrnd(MU, SIGMA)';
%Ausfall = 1, Nichtausfall = 0
zufallsMat(zufallsMat<=PD)=1;
zufallsMat(zufallsMat<1)=0;
temp = zufallsMat.*AV*LGD;
F(1,i) = nansum(temp.*weigth);
auf den ersten Blick schwierig.
Kannst du Testdaten zur Verfügung stellen?
Hast du mal den Profiler drüberlaufen lassen um zu sehen, welche Zeilen die meiste Zeit brauchen?
Du kannst Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen. Du kannst Dateien in diesem Forum posten Du kannst Dateien in diesem Forum herunterladen
MATLAB, Simulink, Stateflow, Handle Graphics, Real-Time Workshop, SimBiology, SimHydraulics, SimEvents, and xPC TargetBox are registered trademarks and The MathWorks, the L-shaped membrane logo, and Embedded MATLAB are trademarks of The MathWorks, Inc.