3. PowerBASIC und der CoProzessor
3.1. Unterstützt PowerBASIC einen CoProzessor?
3.2. Welche Fließkommabibliothek ist für mich die optimale?
3.3. Macht sich der CoProzessor auch bei $FLOAT PROCEDURE bemerkbar?
3.4. Welche PowerBASIC-Funktionen sind betroffen?
3.5. mögliche Ursachen für den CoProzessor-Effekt
3.6. PowerBASIC-Benchmark Source
3.1. Unterstützt PowerBASIC einen CoProzessor?
Die Antwort lautet ganz einfach JA. Bereits in den Compiler-Optionen
haben Sie die Möglichkeit drei verschiedene Fließkommabibliotheken
auszuwählen. Aber mit dieser Unterstützung des CoProzessors sind unter
PowerBASIC einige Haken verbunden, die es hier aufzuzeigen geht damit
sie schnellere und hoffentlich nur sauber funktionierende Programme
entwickeln.
3.2. Welche Fließkommabibliothek ist für mich die optimale?
PowerBASIC bietet insgesamt drei Fließkommabibliotheken an:
- $FLOAT EMULATE
Dieses ist die DEFAULT-Bibiothek und dadurch in der Regel bei ca 95%
der Anwender im Einsatz. Sie zeichnet sich durch automatische
Unterstützung des i87 aus, sofern ein solcher CoProzessor gefunden wurde.
Der enorme Nachteil liegt aber sofort auf der Hand. PowerBASIC
testet ständig auf einen CoProzessor und wird dadurch erheblich
langsamer sofern wirklich kein i87 installiert ist.
- $FLOAT PROCEDURE
Diese Bibliothek ist mit Abstand die schnellste, sofern kein
CoProzessor installiert ist.
- $FLOAT NPX
Mit Abstand liegt diese Bibliothek vorne, sofern jedenfalls ein
i87 vorhanden ist.
3.3. Macht sich der Coprozessor auch bei $FLOAT PROCEDURE bemerkbar?
Auch hier ist die Antwort ganz klar: JA. Obwohl PowerBASIC keine
Coprozessorunterstützende Fließkommabibliothek eingebunden hat, läßt
sich dies anhand der beiliegenden Source sehr deutlich beweisen.
Des Rätsels Ursache liegt in der PowerBASIC-Runtime-Bibliothek, diese
unterstützt schon von Hause aus einen i87. Deshalb lassen sich auch die
Geschwindigkeitsauswirkungen der verschiedenen Fließkommabibliotheken
nicht verallgemeinern.
3.4. Welche PowerBASIC-Funktionen sind betroffen?
Als betroffende internen PowerBASIC-Funktionen wäre da vorallem
SELECT CASE zu nennen. Messungen haben gezeigt das SELECT CASE mit i87
5 Sekunden braucht. Unter den gleichen Voraussetzungen (aber ohne i87)
bis zu 200 Sekunden benötigen kann ($FLOAT EMULATE). Enorme
Verbesserungen waren bereit mit $FLOAT PROCEDURE zu erreichen (jetzt
nur noch 35 Sekunden).
Im allgemeinen kann gesagt werden: SELECT CASE hat nichts in
zeitkritischen Routinen zu suchen.
Ebenso, aber bei weitem nicht so extrem, ist die PRINT-Ausgabe von
nummerischen Ausdrücken betroffen.
3.5. mögliche Ursachen für den CoProzessor-Effekt
Meines Erachtens hat dieser Effekt recht natürliche Ursachen, da alle
betroffenden Befehle mit dem neuen 80-stelligen Zahlensystem umgehen
können müssen. Anscheinend haben die Programmierer von PowerBASIC
versucht den zusätzlichen Rechenaufwand durch Einsatz des CoProzessors
wieder wettzumachen. Wobei Ihnen dies bei vorhandenem CoProzessor
auch recht gut gelungen ist.
Andererseits kann man sich fragen, warum der Compiler dies nicht besser
optimiert sofern nur normale 16/32bit Zahlen eingesetzt werden.
3.6. PowerBASIC-Benchmark Source
Die hier beigefügte Source soll Ihnen die Zusammenhänge der vorherigen
Abschnitte besser demonstrieren. Am besten Sie compilieren die Source
und testen einfach ein wenig herum.
Source:
REM *****************************************************************
REM
REM PBBENCH.EXE - Performance-Meáprogramm fr PowerBASIC
REM
REM Zum Feststellen der Geschwindigkeitsunterschiede einzelner
REM PowerBASIC-Befehle in Verbindung der verwendeten Flieákomma-
REM bibiothek bei vorhanden bzw. nichtvorhandensein eines
REM Coprozessors.
REM
REM Copyright: Thomas Gohel & Andras Hoeffken Version 2.10
REM Alle Rechte vorbehalten
REM
REM -----------------------------------------------------------------
REM
REM Wichtige Hinweise:
REM Fr halbwegs reale Meáungen muá sich der Prozessor im REAL-
REM Mode befinden. Es drfen keine TSR-Treiber installiert sein,
REM also kein KEYB.COM, SMARTDRV.EXE oder ähnliches.
REM Wichtig:
REM Fr eine halbwegs genaue Meáung muá das Programm mehrmals
REM aufgerufen und dann Mittelwerte gebildet werden.
REM
REM Besitzer von 486'er/586'ern bzw. 286/386'ern mit
REM installiertem x87'er Prozessor k”nnen den CoProzessor fr
REM PowerBASIC mit dem in dieser Source beigefgtem Listing
REM Ein/Aus schalten. Das muá allerdings die IDE bzw. das fertige
REM EXE-File erneut gestartet werden!
REM
REM *************************************************************
REM
$COMPILE EXE "PBBENCH.EXE"
$CPU 80386
$LIB ALL OFF
REM $FLOAT NPX ' fr Rechner mit CoProzeáor (am
' schnellsten)
REM $FLOAT PROCEDURE ' fr Rechner ohne CoProzeáor (von mir
' empfohlen)
REM $FLOAT EMULATE ' automatisch untersttzen (ohne
' Coprozessor extrem langsam!!
REM $DEBUG MAP OFF
PRINT
PRINT "Performance-Messprogramm fuer PowerBASIC";:
PRINT TAB(58); "(c) A.Hoeffken/Th.Gohel";:
PRINT TAB(68); "Version 2.10";:
PRINT STRING$(80,"-");
PRINT
a% = 1 ' diverse Variablen
i% = 1234 ' -"-
e& = 12345678 ' -"-
REM Zc1! fr 5000000-Schleifen ; zum Herausrechnen der Zeiten
REM Zc2! fr 2000000-Schleifen ; fr die FOR/NEXT-Schleifen
REM Zc3! fr 100000-Schleifen
REM Zc4! fr 2000-Schleifen
IF pbvnpx > 0 THEN
PRINT "CoProzessor " + CHR$(pbvnpx+48) + "87 gefunden!"
PRINT
PRINT "Soll der CoProzessor fr die n„chste Messung ";
PRINT "ausgeschaltet werden (J/N)?"
BEEP
A$ = UCASE$(INPUT$(1))
IF A$ = "J" THEN CoPro "AUS"
ELSE
PRINT "kein CoProzessor gefunden!"
PRINT
PRINT "Soll der CoProzessor fr die n„chste Messung ";
PRINT "wieder eingeschaltet werden (J/N)?"
PRINT
PRINT "Hinweis: Einschalten dieses Testes bei nicht ";
PRINT "installiertem Coprozessor fhrt"
PRINT " zum Absturz!"
BEEP
A$ = UCASE$(INPUT$(1))
IF A$ = "J" THEN CoPro "EIN"
END IF
PRINT
GOSUB HoleZeitKonstanten
GOSUB MesseFORNEXT
GOSUB MesseIFTHEN
GOSUB MesseSELECTCASE
GOSUB MesseMATHEMATIK
GOSUB MesseSTRING
GOSUB MesseNUMPRINT
GOSUB MesseSTRPRINT
PRINT
END
'********************************************************************
' Holen der einzelen Zeitkonstanten fr die einzelnen Messungen
'********************************************************************
HoleZeitKonstanten:
PRINT "Messung der Zeitkonstanten ";
t1! = TIMER
FOR i& = 1 TO 2000 ' Zeit fr 2000-Schleifen ausmessen
NEXT i&
t2! = TIMER
Zc4! = t2! - t1!
PRINT ".";
t1! = TIMER
FOR i& = 1 TO 5000000 ' Zeit fr 5-Mio-Schleifen ausmessen
NEXT i&
t2! = TIMER
Zc1! = t2! - t1!
PRINT ".";
t1! = TIMER
FOR i& = 1 TO 100000 ' Zeit fr 100000-Schleifen ausmessen
NEXT i&
t2! = TIMER
Zc3! = t2! - t1!
PRINT "."
t1! = TIMER
FOR i& = 1 TO 2000000 ' Zeit fr 2-Mio-Schleifen ausmessen
NEXT i&
t2! = TIMER
Zc2! = t2! - t1!
RETURN
'********************************************************************
'
' Hier nun die einzelnen Routinen zur Zeitmessung der einzelnen
' Befehle. Im Prinzip ist die Messung immer abh„ngig vom verwendeten
' Computersystem und dem installiertem Betriebssytem. Gerade aber in
' Verbindung mit PowerBASIC, der verwendeten Flieákommabibiliothek
' und dem Vorhandensein eines CoProzessors lassen sich erhebliche
' Unterschiede bei der Performance einzelner Befehle ermitteln.
'
'********************************************************************
MesseFORNEXT:
PRINT "Messe FOR/NEXT : ";
t1! = TIMER
FOR i& = 1 TO 5000000 '5-Millionen-Schleife ausmessen,
NEXT i& 'i = long integer
t2! = TIMER
PRINT t2! - t1!; "sec "
RETURN
MesseIFTHEN:
PRINT "Messe IF/THEN : ";
t1! = TIMER
FOR i& = 1 TO 5000000
IF a% = 0 THEN 'IF THEN Methode
ELSEIF a% = 2 THEN
ELSE
END IF
NEXT i&
t2! = TIMER
PRINT t2! - t1! - Zc1!; "sec "
RETURN
MesseSELECTCASE:
PRINT "Messe SELECT CASE: ";
t1! = TIMER
FOR i& = 1 TO 2000000
SELECT CASE A% 'SELECT CASE Methode
CASE 0
CASE 1
CASE ELSE
END SELECT
NEXT i&
t2! = TIMER
PRINT t2! - t1! - Zc2!; "sec "
RETURN
MesseMATHEMATIK:
PRINT "Messe MATHEMATIK : ";
t1! = TIMER
FOR i& = 1 TO 2000000
i% = i% + 100 'extrem einfache Aufgaben
e& = e& * 2
e& = e& \ 2
i% = i% - 100
NEXT i&
t2! = TIMER
PRINT t2! - t1! - Zc2!; "sec "
RETURN
MesseSTRING:
PRINT "Messe STRING's : ";
t1! = TIMER
FOR i& = 1 TO 2000
A$ = STRING$(20000, 32)
A$ = RIGHT$(A$, 10000) + "Test"
e% = INSTR(A$, "Test")
A$ = ""
NEXT i&
t2! = TIMER
PRINT t2! - t1! - Zc4!; "sec "
RETURN
MesseNUMPRINT:
PRINT "Messe NUM-PRINT's ";
t1! = TIMER
FOR i& = 1 TO 100000
LOCATE , 1
PRINT "Messe NUM-PRINT's: "; i&
NEXT i&
t2! = TIMER
LOCATE , 20
PRINT t2! - t1! - Zc3!; "sec "
RETURN
MesseSTRPRINT:
PRINT "Messe $$$-PRINT's ";
t1! = TIMER
FOR i& = 1 TO 100000
LOCATE , 1
PRINT "Messe $$$-PRINT's: ";
NEXT i&
t2! = TIMER
LOCATE , 20
PRINT t2! - t1! - Zc3!; "sec "
RETURN
'*****************************************************************
' Hier nun die Routine zum Ausschalten des Coprozessors
'************************************************************************
SUB Copro(Switch$)
SELECT CASE UCASE$(Switch$)
CASE "AUS", "OFF", "-"
! mov ax, &h0040
! mov es, ax
! mov ax, word ptr es:[&h10]
! and ax, &b1111111111111101
! mov word ptr es:[&h10], ax
CASE "EIN", "ON", "+"
! mov ax, &h0040
! mov es, ax
! mov ax, word ptr es:[&h10]
! or ax, &b0000000000000010
! mov word ptr es:[&h10], ax
END SELECT
END SUB