FuncSub
Während ich nach einer Möglichkeit gesucht habe, die Performance von
Geniustrader zu verbessern, ist mir aufgefallen, dass sehr viel Zeit für
diverse Funktionsaufrufe draufgeht, deren Funktion lediglich darin besteht, einen
einzelnen Wert zurückzugeben.
Dies ist das Resultat das streng objektorientierten Programmierweise, die Geniustrader
zu Grunde liegt. Diese ist zwar sehr sinnvoll, um den Code lesbar zu halten, führt
jedoch auch dazu, dass durch die vielen Funktionsaufrufe die Performance leidet. Hier
ist das Ergebnis eines etwas umfangreicheren Backtests mit dem DProf-Module analysiert
(dabei wude das DirtyKill-Modul verwendet, so dass die Carp::Datum-Aufrufe eliminiert sind):
Tested ... ok in 79 seconds
Total Elapsed Time = 20.27753 Seconds
User Time = 14.72753 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c Name
74.2 10.92 7.779 157523 0.0000 0.0000 GT::CacheValues::is_available
44.9 6.620 11.841 194946 0.0000 0.0001 GT::ArgsTree::get_arg_values
38.8 5.719 3.816 952019 0.0000 0.0000 GT::Registry::get_name
37.5 5.529 2.043 174341 0.0000 0.0000 GT::Calculator::indicators
36.8 5.426 6.158 506514 0.0000 0.0000 GT::Indicators::Prices::calculate
22.0 3.246 3.968 201000 0.0000 0.0000 GT::Indicators::Generic::Eval::cal
culate
21.6 3.186 4.358 13517 0.0002 0.0003 GT::Dependency::dependencies_are_a
vailable
18.7 2.764 17.552 8002 0.0003 0.0022 GT::Indicators::calculate_interval
9.36 1.379 7.435 163164 0.0000 0.0000 GT::Indicators::__ANON__
9.23 1.359 3.472 40023 0.0000 0.0001 GT::Dependency::days_required
9.17 1.350 0.962 193972 0.0000 0.0000 GT::CacheValues::get
7.99 1.177 24.249 2502 0.0005 0.0097 GT::Indicators::SMA::calculate
6.65 0.980 0.953 13743 0.0001 0.0001 Carp::Datum::DTRACE
5.70 0.839 0.847 557 0.0015 0.0015 Carp::Datum::Strip::strip
Man kann leicht sehen, dass ein grosser Teil der Laufzeit z.b. von der Funktion
GT::CacheValues::is_available verbraucht wird. Diese gibt letztlich nur einen
Wert zur¨ck, nämlich ob ein Hashelement definiert ist oder nicht. Dadurch, dass
diese Funktion ca. 160.000 Mal aufgerufen wird, entsteht ein unnötiger Overhead.
Nachdem Download des Skriptes, sollte man das GT-Verzeichnis nach /tmp kopieren und
das Skript auf alle Dateien wie folgt anwenden:
for i in $(find /tmp/GT -name '*.pm')
do
cp $i /tmp/funcsub.tmp
perl funcsub.pl /tmp/funcsub.tmp > $i
done;
Nun ist noch ein use lib '/tmp';-Statement in das auszuführende Programm
einzufügen und schon sieht die Performance so aus (dabei ist der Aufruf zu DirtyKill
- im Gegensatz zu oben nicht in der Laufzeit enthalten):
Tested ... ok in 41 seconds
Total Elapsed Time = 27.65204 Seconds
User Time = 28.04077 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c Name
18.5 5.199 9.895 194946 0.0000 0.0001 GT::ArgsTree::get_arg_values
13.3 3.750 3.305 506514 0.0000 0.0000 GT::Indicators::Prices::calculate
8.56 2.399 2.520 201000 0.0000 0.0000 GT::Indicators::Generic::Eval::cal
culate
7.91 2.219 2.416 13517 0.0002 0.0002 GT::Dependency::dependencies_are_a
vailable
6.60 1.852 10.603 8002 0.0002 0.0013 GT::Indicators::calculate_interval
5.53 1.550 1.356 193972 0.0000 0.0000 GT::CacheValues::get
5.10 1.430 6.421 163164 0.0000 0.0000 GT::Indicators::__ANON__
4.28 1.199 4.346 40023 0.0000 0.0001 GT::Dependency::days_required
3.71 1.040 17.051 2502 0.0004 0.0068 GT::Indicators::SMA::calculate
3.21 0.899 65.776 6002 0.0001 0.0110 GT::Dependency::compute_dependenci
es
2.96 0.830 0.671 158817 0.0000 0.0000 GT::ArgsTree::get_nb_args
2.57 0.720 0.706 13743 0.0001 0.0001 Carp::Datum::DTRACE
2.50 0.700 0.640 59542 0.0000 0.0000 GT::Dependency::get_indicator_depe
ndencies
Wie unschwer zu erkennen ist, hat sich die Performance um fast 50% verbessert. ...und
es können sicherlich noch weitere Funktionen auf diese Art und Weise ersetzt werden
(Feedback ist wie immer willkommen).
Es gibt jedoch zu beachten, dass das Skript nur auf der Basis von regulären
Ausdr¨cken arbeitet und deshalb leicht Fehler erzeugen kann. Für die Zukunft
wäre es sicher sinnvoll, z.B. auf der Basis des Modules B::Deparse einen echten
Parser zu schreiben, der zum einen in der Lage ist, selbst nach Funktionen zu suchen
und zum anderen z.B. den Typ des aufrufenden Objects berücksichtigt.
Die Funktionalität dieses Moduls ist nun in das Modul
OptimizeGT.pm integriert. Dieses ist mittlerweile Teil der offiziellen Distribution.
Download von funcsub.pl
|