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
 
		   |