Quick-Tips

Zeitmessung portabel

Haben Sie auch schon in einem C-Programm den Algorithmus überarbeitet und dann wissen wollen, wieviel schneller die zeitkritische Routine jetzt läuft?

Sicher haben Sie bisher meist die Stoppuhr genommen, das alte Programm gestartet (natürlich mehrmals, denn die Knöpfe an der Stoppuhr sind so viele und so unhandlich klein), die von Hand gestoppte Zeit aufgeschrieben und das ganze für das neue Programm wiederholt. Die Programmierung einer Meßroutine hätte schließlich auch gedauert, vor allem was die Ausgaberoutine für die Zeit betrifft, und dann sollte das Programm schließlich auch unter DOS laufen, also keine BIOS-Aufrufe und Zugriffe auf Systemvariablen enthalten. Das nebenstehende Programmfragment erledigt die Zeitmessung souverän mit der höchstmöglichen Genauigkeit und ist nicht systemabhängig. Es läuft mit Pure C unter TOS genauso wie mit Turbo C++/Borland C++ unter DOS (bei anderen Systemen siehe Kommentar zu prttime). Einmal abgetippt (nur #include-Zeilen und Funktion prttime) und als PRTTIME.C abgespeichert, können Sie in jedem Programm mit ein paar Zeilen die Zeitmessung implementieren. Die Funktion prttime können Sie entweder über #inklude „prttime.c“ oder mit derBlocklese-/-kopierfunktion des Editors in Ihr Programm einbinden. Die Zeitmessung erfolgt dann wie hier in der Funktion main angedeutet.

Rüdiger Lause

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

/* (c)by MAXON-Computer/**Autor:** Rüdiger Lause */
/* prttime gibt die Zeit, die ticks */
/* Clockticks der Systemuhr entspricht, in */
/* Stunden, Minuten, Sekunden und den nach */
/* CLKTCK meßbaren Sekundenbruchteilen aus. */ 
/* ticks kann über Subtraktion der Ergebnisse */ 
/* zweier clock-Aufrufe ermittelt werden. */
/* Achtung: ticks darf nicht negativ sein! */
/* Voraussetzungen: Der Typ clock__t muß dem Typ */ 
/* long (unsigned int, int) entsprechen. Die */ 
/* Konstante CLK_TCK muß ganzzahlig sein und im */ 
/* Wertebereich des Typs long liegen. */

void prttime(long ticks)
{
    long
        ticks_per_sec = CLK_TCK,
        remaining,
        denominator;
    int
        index;
    char
        *name[4] = {" h", " min", " sec", "/1234567890 sec" };
    long
        unit[4] = {3600L, 60L, 1L, 1L};
    ldiv_t result;

/* Meßbare Sekundenbruchteile bei */
/* Ausgabe und Einheiten beachten */
    sprintf(name[3]+1, "%1d sec", ticks_per_sec);
    for(index=0; index<3; index++)
        unit[index] *= ticks_per_sec;
/* Zeit in Stunden, Minuten, Sekunden und den */ 
/* meßbaren Sekundenbruchteilen ausgeben */
    remaining = ticks;
    for(index=0; index<4; index++) {
        denominator = unit[index];
        result = ldiv(remaining, denominator);
        if(result.quot || index==3)
            printf("%1d%s ", result.quot,name[index]);
        remaining = result.rem;
    }
}
...
/* Beispiel für den Aufruf der Funktion prttime */ 
int main(void)
{
    clock_t Start, finish;
    ...
/* Systemzeit vorher */
    start = clock();
/* Aufruf der Funktion, deren Rechen- */
/* zeit bestimmt werden soll          */
    calc();
/* Systemzeit hinterher */
    finish = clock();
/* Zeitausgabe */
    prttime((long)finish - (long)start);
/* Warten auf Tastendruck */
    getchar();
/* Ende */
    return 0;
}

Große Optimierung in GFA-BASIC möglich!

Beim Feldkopieren in GFA-BASIC ist eine sehr große Optimierung möglich, wenn man nicht, wie im Normalfall, in einer Schleife Feldelement n von Feld1 mit Feldelement n von Feld2 in gleichsetzt, sondern die Anzahl der Bytes, die ein Feld (hintereinander!) im Speicher belegt, einfach in das andere Feld mit Hilfe von BMOVE kopiert. Zu beachten ist, daß man die Anzahl der Feldelemente mit dem Speicherbedarf eines Elementes in Bytes multiplizieren muß (Angabe bei BMOVE). So ist eine Geschwindigkeitssteigerung von nahezu 10000% (9250!), also um Faktor 100 möglich. Während die erste Kopiervariation (siehe Fisting) 1.85 Sekunden benötigt, ist es bei der zweiten mit 0.02 Sekunden getan.

Matthias Brust/Christian Roth

' Optimierung beim Feldkopieren
' (c)1993 by MAXON-Computer
' Autoren: Matthias Brust/Christian Roth
' Für jede GFA-BASIC Version!!!
'
DIM ausgangsfeld%(10000)
DIM zielfeld_normal%(10000)
DIM zielfeld_bmove%(10000)
'
FOR i%=0 TO 9999
    ausgangsfeld%(i%)=i%
NEXT i%
'
t=TIMER
FOR i%=0 TO 9999
    zielfeld_normal%(i%)=ausgangsfeld%(i%)
NEXT i%
PRINT (TIMER-t)/200 
’
' "V:" Holt Adresse des Null-Feldes, also des Anfangs
t=TIMER
BMOVE V:ausgangsfeld%(0),V:zielfeld_bmove%(0),4*10000 
PRINT (TIMER-t)/200 
'
GEMSYS 20
FOR i%=0 TO 9999
    PRINT zielfeld__normal%(i%)'zielfeld_bmove%(i%)
NEXT i%

Betrachten des Programm-Headers

Das Betriebssystem muß zum Starten eines Programms viele Daten wissen, z.B. die Länge des Text- oder Datensegments. Deshalb schreibt das Betriebssystem vor jedes Programm einen kleinen Vorspann, welcher von erfahrenen Usern dazu genutzt werden kann, die Segmente zu analysieren. Weiterhin sind diese Werte für das Betriebssystem notwendig, um genügend Speicher zu reservieren. Mit dem kleinen abgedruckten Programm können Sie diesen Header betrachten. Außerdem kann die Symboltabelle mit Hilfe der ausgegebenen Werte betrachtet werden, die von vielen Compilern oder Assemblern wahlweise erzeugt wird. Damit läßt sich mit entsprechenden Tools, welche die Symboltabelle unterstützen (z. B. SID), die Analyse von Programmen wesentlich vereinfachen.

Christian Roth/Matthias Brust

' Betrachten des Programmheaders
' unter GFA-BASIC V3.X
' (c)1993 by MAXON-Computer
' Autoren: Christian Roth u. Matthias Brust
'
FILESELECT "\*.*","",file$
IF file$<>"" AND EXIST(file$)
    OPEN "i",#1,file$
    speicher$=SPACE$(28)
    speicher%=V:speicher$
    BGET #1,speichert,28
    PRINT "MAGIC-Word: ";HEX$(CARD{speichert})
    PRINT "Länge Text-Segment: ";LONG{speicher%+2}
    PRINT "Länge Data-Segment: ";LONG{speicher%+6}
    PRINT "Länge BSS-Segment: ";LONG{speicher%+10}
    PRINT "Länge Symboltabelle: ";LONG{speicher%+14}
    PRINT "ph_res1 (reserviert: 0): ";LONG{speicher%+18}
    PRINT "ph_res2 : ";LONG{speicher%+22}
    PRINT "ph_flag (reserviert: 0): ";CARD{speicher%+26}
    CLOSE #1
ENDIF

„Crawcin()“ gegen „getch()“

Oftmals wird in Programmen, die in C geschrieben sind, die Funktion „getch()“ verwendet, um dem Benutzer die Möglichkeit zu geben durch Tastendruck selbst zu bestimmen, wann der Programmablauf fortgesetzt werden soll. Aber auch „Crawcin()“ tut diesen Dienst. Sogar besser als „getch()“! Der Code für „Crawcin()“ ist um die Hälfte kürzer als der von „getch()“ (siehe unten). „getch()“ liest wie „Crawcin“ ein Zeichen von der Tastatur und speichert es in einem Word. Als Ergebnis von „Crawcin()“ erhält man im unteren Word den ASCII-Code des Zeichens, im oberen Word den Scan-Code. Möchte man nun die überflüssigen Bytes in seinen alten Programmen entfernen, kann man dies mit: #define getch() Crawcin() ohne große Umschweife dem Compiler (Präprozessor) mitteilen. Anmerkung: die Funktion Crawcin() sollte im allgemeinen in der Header-Datei „OSBIND.H“ und bei Turbo C bzw. Pure C in der Datei „TOS.H“ zu finden sein.

Matthias Brust/Christian Roth

Getch:
$00092B8E   MOVE.W  $00092D28,D0
$00092B94   BNE.B   $00092BAE
$00092B96   MOVE.L  A2,-(A7)
$00092B98   MOVE.W  #$0008,-(A7)
$00092B9C   TRAP    #1
$00092B9E   ADDQ.W  #2,A7
$00092BA0   MOVEA.L (A7)+,A2
$00092BA2   TST.W   D0
$00092BA4   BNE.B   $00092BAC
$00092BA6   MOVE.L  D0,$00092D28
$00092BAC   RTS
$00092BAE   CLR.W   $00092D28
$00092BB4   RTS

Crawcin:
$00092BB6   MOVE.L  A2,-(A7)
$00092BB8   MOVE.W  #$0007,-(A7)
$00092BBC   TRAP    #1
$00092BBE   ADDQ.W  #2,A7(SP)
$00092BC0   MOVEA.L (A7)+,A2
$00092BC2   RTS


Aus: ST-Computer 03 / 1993, Seite 120

Links

Copyright-Bestimmungen: siehe Über diese Seite