Buchempfehlung
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Mikrocomputertechnik mit Controllern der Atmel AVR-RISC-Familie
Umfassend, aber leicht verständlich führt dieses Buch in die Programmierung von ATMEL AVR Mikrocontrollern ein. [Mehr Infos...]


Tutorial

Ausführungsgeschwindigkeit bestimmen

von RedakteurytwinkySeite 1 von 1

Auch beim Programmieren gibt es meist mehrere Wege, die zum Ziel führen.
Aber nicht immer, wenn wir das Ziel erreicht haben, sind wir auch da, wo wir hin wollten.
Es gibt tatsächlich Probleme, die sich nicht durch Nachschlagen in der Befehlsreferenz lösen lassen.
Effektive Programmierung war früher ein extrem wichtiges Thema, damals, als ein Prozessor noch 4,75Mhz hatte und der verfügbare Speicher manchmal nur 640Kb und (machmal viel)weniger betrug(Schlagwort "Mehr als 640Kb braucht kein Mensch", fälschlich oft Bill Gates zugeschoben). Doch auch heute im Zeitalter der GHz-Prozessoren und GB-Ram-Speicherplatz macht es Sinn, darüber nachzudenken. Naja, eine Anleitung, wie die Profiling-Fähigkeiten von FreeBASIC genutzt werden können, wird dies nicht. Aber auf alle Fälle, gibt es einen besseren Weg, als mit dem FB-Timer Ausführungszeiten zu messen. Die sind nämlich nicht unbedingt aussagekräftig genug, weil ein moderner Rechner eben noch anderes macht, als 'nur' Programme auszuführen. Nehmen wir als Beispiel nur mal den Wächter von Antivir: Er läuft (meist) ständig mit und benötigt auch Prozessorzeit. Nunja, er läßt sich (auch auf Dauer) abschalten, aber auf Kosten anderer Vorteile. Und alle 'überflüssigen' Programme auszuschalten, nur um ein Programm zu testen, ist relativ praxisfern. Fangen wir einfach mal ganz langsam an:

Dim As Double StartZeit, EndZeit 'brauchen wir für die Zeitmessung
StartZeit=Timer 'Anfangszeit festhalten
Sleep 1 'soll eine 1 Millisekunde warten
EndZeit=Timer 'Endzeit stoppen, in der Annahme, daß der Aufwand gleich ist und rausfällt^^
?"Das hat ";
?Using"#.############" ;EndZeit-Startzeit;
?" gedauert"

Durch das Print Using(..) sehen wir schon mal eine 'lesbare' Zahl, die jedoch erstmal nichts aussagt. Aber wir wissen ja, daß Sleep in Millisekunden angegeben wird, also multiplizieren wir das Ergebnis mit 1000, um das Ergebnis in Sekunden zu erhalten, die Nachkommastellen geben halt die Bruchteile einer Sekunde an.

?Using"#######.###" ;(EndZeit-Startzeit)*1000;

Jetzt können wir die 'Zeitmessung' benutzen.
Was messen wir als Erstes? Nehmen wir doch einfach eine ganz simple Wertzuweisung. Als Anwort auf alle Fragen, weisen wir 42 mal der Integervariablen Hal zu:

Dim As Double StartZeit, EndZeit 'brauchen wir für die Zeitmessung
Dim Hal As Integer 'eine Testvariable, Hal würde sich wahrscheinlich beschweren^^
StartZeit=Timer 'Anfangszeit festhalten
Hal=42 'Wertzuweisung an Hal
EndZeit=Timer 'Endzeit stoppen, in der Annahme, daß der Aufwand gleich ist und rausfällt^^
?"Das hat ";
?Using"#######.###" ;(EndZeit-Startzeit)*1000;
?" gedauert"
Sleep

Wow, 1 Millisekunde! Aber mehrere 'Messungen' geben höchst unterschiedliche Werte. Was tun?
MichaelW aus dem englischen Forum hat dazu ein Programm vorgestellt, das nicht die Zeit mißt, sondern die Anzahl der Taktzyklen, die ein Befehl benötigt(oder eine Befehlsfolge). Nein, jetzt folgt keine Abhandlung über Prozessorinterna..
(Obwohl das vielleicht gar nicht so verkehrt wäre.)
Counter.Bas erhöht die Prozess-Priorität des aktuellen Prozesses und setzt sie hinterher wieder auf Normal. Damit klappt die Bestimmung ganz gut. Nochmal: Jetzt wird keine Zeit mehr gemessen, sondern wieviel Takte der Prozessor zur Abarbeitung eines Befehls (etwa) benötigt. Das klingt doch schon mal ganz gut! Wollen wir das mal ausprobieren?
Klar doch:

#include once "windows.bi"
#include "counter.bas"
Const sf="######", wo=17, LoopCounterValue=2000
Dim As uInt i, y=2, z = 3
Dim As Double d, d2=Atn(1)
Dim As String s="Gallia est omnis divisa in partes tres..", c
?"Befehl";Tab(wo+1);" Zyklen"
Counter_Begin (LoopCounterValue, HIGH_PRIORITY_CLASS)
i=1
counter_end
?"i = 1";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=i+2
counter_end
?"i = i + 2";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i+=2
counter_end
?"i+= 2";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=i * 3
counter_end
?"i = i * 3";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i*=3
counter_end
?"i*= 3";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=i*4
counter_end
?"i = i * 4";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i*=4
counter_end
?"i*= 4";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=i/3
counter_end
?"i = i / 3";:?Tab(Wo);:?Using sf$;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i/=3
counter_end
?"i/= 3";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=i/4
counter_end
?"i = i / 4";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i/=4
counter_end
?"i/= 4";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=i\3
counter_end
?"i = i \ 3";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i\=3
counter_end
?"i\= 3";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=i\4
counter_end
?"i = i \ 4";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i\=4
counter_end
?"i\= 4";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=y/z
counter_end
?"i = y / z";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=y\z
counter_end
?"i = y \ z";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=y mod z
counter_end
?"i = y mod z";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
d=d2^z
counter_end
?"d = y ^ z";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
d=d2*d2*d2
counter_end
?"d = y * y * y";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
d=d2^3
counter_end
?"d = y ^ 3";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
i=sqr(y)
counter_end
?"i = sqr(y)";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
d=sin(d2)
counter_end
?"d = sin(d2)";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
c=s[0]
counter_end
?"c = s[0]";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
c=Mid(s, 1, 1)
counter_end
?"c = Mid(s, 1, 1)";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
c=Left(s, 1)
counter_end
?"c = Left(s, 1)";:?Tab(Wo);:?Using sf;counter_cycles

counter_begin(LoopCounterValue, HIGH_PRIORITY_CLASS)
c=Right(s, 1)
counter_end
?"c = Right(s, 1)";:?Tab(Wo);:?Using sf;counter_cycles;
sleep

Genauso, wie wir es beim Timer gemacht haben, müssen wir es bei Counter.bas auch machen:

Startzeit festhalten
  Anweisung ausführen
EndZeit festhalten
Ergebnis ausgeben

In diesem Programm habe ich einige Anweisungen ausprobiert(nur heißt die Variable nicht Hal, sondern i), deren Ergebnisse interessant sind:

i=i+1
i+=1

unterscheiden sich in der Taktanzahl nicht großartig. Spannender wird es bei '*': Hier gibt es deutliche Unterschiede zwischen geraden und ungeraden Zahlen, die sich aber damit begründen lassen, daß hier Prozessor-interne Befehle verwendet werden(bei geraden Zahlen) und die sind nunmal schneller..
Lassen wir unseren Blick über das Ausgabe-Protokoll schweifen, stellen wir fest, daß bei der Division die Anzahl der Taktzyklen steigt. Warum das denn? Eine mögliche Erklärung ist hier, daß bei der Division ja noch die Fehlerprüfung dazu kommt, denn der Prozessor muß ja prüfen, ob er nicht gerade durch 0 teilen soll. Da das noch nicht mal Mathematiker können, wie soll ein Prozessor das dann können, da er ja auch nur 'programmiert' ist.
Lassen wir unseren Blick etwas weiter schweifen, fällt sofort der nächste Außreißer auf: ^ der 'Exponentiator'
Der haut ja nun unheimlich 'rein, deshalb folgen ja auch gleich zwei Zeilen, die das etwas entschärfen:

d2=Atn(1)
z=3
d = d2 ^ z
d = d2 * d2 * d2
d = d2 ^ 3

Die drei letzten Anweisungen berechnen alle dasselbe, aber wie sehen denn die Taktzyklen aus?
Zum Einen liegt das natürlich daran, daß d2 doppelt-genau ist(Double). Somit zeigt dies, daß Double's den Prozessor ganzschön fordern.
Zum Schluß folgen noch ein paar 'Untersuchungen zum Thema 'Strings'. Wie zu erwarten, schlägt die Pointer-Schreibweise mit relativ wenig Taktzyklen zu Buche, das ist ja auch ganz in Ordnung so..
Wie sieht es denn nun mit meinem 'Liebling' Mid() aus? Boah, das haut ja noch mehr rein als '^'..
Deshalb habe ich Left() gleich dahinter platziert und wir stellen fest, daß Mid() und Left() sich nicht großartig unterscheiden. Der Wert für Right() steht hier nur zur Vervollständigung und zum Vergleich.
Fazit: Counter.Bas ist kein Werkzeug, das effizienten Programmcode erzeugen kann.
(Dafür habe ich meinen Programmgenerator..
..zwischen den Ohren)^^
Es kann aber ein wertvolle Hilfe sein, wenn ich die klassischen Wege schon alle beschritten habe:
Sowenig Doubles wie möglich verwenden, Berechnungen NICHT IN Schleifen durchführen, sondern wo es geht DAVOR.
Soweit läßt es sich pauschal sagen, alles Weitere hängt vom jeweiligen Programm ab.
So, fertich..
Hab' ich noch was vergessen? Achso ja, hier ist der Link für Externer Link!Counter.bas. Der führt ins englische Forum. Wer sich dort nicht zurecht findet, kann natürlich auch hier im FreeBASIC-Portal eine Version finden ;-))
Das Beispielprogramm ist so geschrieben, daß Counter.Bas von MichaelW ohne Änderungen läuft..
So, nun aber fröhliches Zählen xD
Gruß
ytwinky

 

Zusätzliche Informationen und Funktionen
  • Das Tutorial wurde am 22.08.2007 von Redakteurytwinky angelegt.
  • Die aktuellste Version wurde am 19.05.2010 von RedakteurVolta gespeichert.
  Bearbeiten Bearbeiten  

  Versionen Versionen