Variablenbezüge in Assembler innerhalb Pascal

Diskussion zum Thema Programmierung unter DOS (Intel x86)
freecrac
DOS-Guru
Beiträge: 861
Registriert: Mi 21. Apr 2010, 11:44
Wohnort: Hamburg Horn

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von freecrac »

zatzen hat geschrieben:Ansonsten bleibt weiterhin die Frage, ob der Player seine vielleicht 10 Schrittweitenberechnungen
pro Puffer nicht doch hinbekommt. Notfalls sogar auch in Hochsprache, d.h. Pascal.
Ich gehe von Rechnern jenseits oder ab 486 aus.
Das sollte man einfach mal ausprobieren.

Dirk
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

freecrac hat geschrieben: Beispiel: Wert mit 10 multiplizieren (ohne MUL-Befehl)!
a) Wert in ein zweites Register retten,
b) das eine Register mit "shl Register, 3" mit 8 multiplizieren
c) das andere Register mit "shl Register, 1" mit 2 multiplizieren
d) beide Register zusammen addieren

Die Shift-Befehle sind im Vergleich zu MUL/DIV sehr schnell
da ich sowieso grade mal wieder dabei war einige routinen zu optimieren:
shl ist ungeeignet da es das carryregister mitverschiebt, besser ist rol (schiebt das oberste bit in das unterste bit und ins carryflag).
zusammen mit der notwendigen Überlaufprüfung (jc) nach ral 3, ist das ganze Kontrukt auf meinem 486 DX2/66 nicht schneller als mit dem Mulbefehl - auf meinem Pentium 120 dagegen gleich mehr als doppelt so schnell
LEA-Befehl
bisher keine Geschwindigkeitssteigerung beobachtet - allerdings habe ich hier auch nur 286er Code.

EDIT: kleinere Revision: je nachdem wie ich die funktionen Anordne ändern sich bei BP7 die Geschwindigkeiten.
Ein

Code: Alles auswählen

function X:word;
 begin;
 ...
 end;

function Y:word;
 begin;
 ...
 end;

begin
 x;
 y;
end.
liefert andere Ergebnisse als

Code: Alles auswählen

function Y:word;
 begin;
 ...
 end;

function X:word;
 begin;
 ...
 end;

begin
 x;
 y;
end.
ka. warum, Worddatenausrichtung ist an, Far aufrufe ebenfalls - Benchmarken ist damit jedenfalls nicht möglich...

Ein tausch der aufrufe (also "begin;y;x;end." statt "begin;x;y;end.") beeinflusst die Geschwindigkeit hingegen nicht.
freecrac
DOS-Guru
Beiträge: 861
Registriert: Mi 21. Apr 2010, 11:44
Wohnort: Hamburg Horn

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von freecrac »

Dosenware hat geschrieben:shl ist ungeeignet da es das carryregister mitverschiebt, besser ist rol (schiebt das oberste bit in das unterste bit und ins carryflag).
Was stört dich daran wenn beim SHL-Behehl das oberste Bit ins Carry-Flag geschoben wird?
Gesetzt wird wird das Carry-Flag ja nur, wenn unser Wert schon zu gross wurde, bzw. unser Zielregister zu klein für das Ergebnis ist.

Asm86faq.txt
SHL verschiebt DEST um SRC Bits nach links. Bei jedem Durchlauf wird das
MSB dabei ins CF geschoben und das LSB gelöscht:

..................
├─────── DEST ────────┤
┌──┐...........┌───┬───┬─ : ─┬───┬───┐.......┌─┐
│ CF │<────■ │MSB │....... : .......│ LSB │<──■│0 │
└──┘...........└───┴───┴─ : ─┴───┴───┘.......└─┘
zusammen mit der notwendigen Überlaufprüfung (jc) nach ral 3, ist das ganze Kontrukt auf meinem 486 DX2/66 nicht schneller als mit dem Mulbefehl
Die Überlaufprüfung mit jc verbraucht vermutlich sehr viel Zeit. Es wäre wohl besser wenn wir nur Werte berechnen, die unser Ziel-Register nicht zum überlaufen bringen können.
- auf meinem Pentium 120 dagegen gleich mehr als doppelt so schnell
Vieleicht wegen Sprungvorhersage damit die Adresse schneller zum Adressbus kommt.
LEA-Befehl
bisher keine Geschwindigkeitssteigerung beobachtet - allerdings habe ich hier auch nur 286er Code.
Mit 286er als Ersatz für Addition:
LEA BX, [BX+SI+o16]
LEA BX, [BX+SI+o8]

;-------

Erst ab 386 mit Scaling von 2,4,8 <sib address>=<Base>+<Index>*(2^(Scale))
Z.B:
LEA EAX, [EBX+ESI*Scale+o32]
LEA EAX, [EBX+ESI*Scale+o8]

LEA EAX, [EAX * 2 + EAX] //EAX = EAX * 3
LEA EAX, [EAX * 4 + EAX] //EAX = EAX * 5
LEA EAX, [EAX * 8 + EAX] //EAX = EAX * 9

Dirk
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

Ok, hatte übersehen das Bit 0 "genullt" wird - Die beschreibung in meinem Assemblerbuch klingt im ersten moment nach rcl.

was die Geschwindigkeit angeht: wie im Edit gesagt - je nachdem wie ich die Unterprogramme anordne ändert sich die Geschwindigkeit doch recht dramatisch, bis ich herausgefunden habe woran das liegt, bzw. wie ich das abstellen kann ist benchen nicht so ohne weiteres möglich.
Die Überlaufprüfung mit jc verbraucht vermutlich sehr viel Zeit. Es wäre wohl besser wenn wir nur Werte berechnen, die unser Ziel-Register nicht zum überlaufen bringen können.
Nur sind die Werte nicht im vornherein bekannt - ich werde wohl mal überprüfen bei welchen Eingangswerten dort der überlauf eintritt, evtl. kann ich den dann bereits vorher abfangen.
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von wobo »

Dosenware hat geschrieben: kleinere Revision: je nachdem wie ich die funktionen Anordne ändern sich bei BP7 die Geschwindigkeiten.
[...]
ka. warum, Worddatenausrichtung ist an, Far aufrufe ebenfalls ...
Wenn Du das nicht schreiben würdest, würde ich sowas nicht glauben ;-)! Vielleicht passt immer nur eine Procedure in den Cache??? Aber trotzdem komisch. Keine Ahnung. Der Source ist wohl zu groß, um ihn zu posten?
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

Habe ich schon mit anderen funktionen getestet: immer das gleiche Ergebnis: die Funktion die zuerst definiert wurde, ist am langsamsten, egal von wo aus sie Aufgerufen wird.
Und das ganze Testprogramm (nur zum Routinenbenchen) passt locker in den L1 des 486er.

ist ja im wesentlichen nur:

gettime(start)
for i:=0 to XXXXX do funktion1;
gettime(mitte)
for i:=0 to XXXXX do funktion2;
gettime(ende)

writeln('Dauer1: ',mitte-start);
writeln('Dauer2: ',ende-mitte);
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von wobo »

Dosenware hat geschrieben:Habe ich schon mit anderen funktionen getestet: immer das gleiche Ergebnis: die Funktion die zuerst definiert wurde, ist am langsamsten, egal von wo aus sie Aufgerufen wird.
Und das ganze Testprogramm (nur zum Routinenbenchen) passt locker in den L1 des 486er.
So, das hat jetzt Zeit gekostet ;-)

hier erstmal das Testprogramm:

Code: Alles auswählen

{$A+,B+,D+,E+,F-,G+,I+,L+,N-,O-,P-,Q+,R+,S+,T-,V+,X+,Y+}

{$M 16384,0,655360}



uses dos;



var   v : word;

      i : longint;



      hStart, mStart, sStart, hsStart,

      hEnde,  mEnde,  sEnde,  hsEnde   : array[1..2] of longint;





{ *** macht aus Integer einen String *** }

function txt( wert:longint ) : string;

var stri:string;

begin

  Str(wert,stri);

  txt:=stri;

end;





procedure StartZeitmessung( Nr:word );

begin

  gettime(Word(hStart[Nr]),Word(mStart[Nr]),Word(sStart[Nr]), word(hsStart[Nr]) );

end;



procedure StopZeit( Nr:word );

begin

  gettime(Word(hEnde[Nr]),Word(mEnde[Nr]),Word(sEnde[Nr]),Word(hsEnde[Nr]));

end;



function ZeitStr( Nr:Word ):string;  { in Hsec }

var Zeitdauer : longint;

    dstr      : string;

begin

{$Q-}

  zeitdauer:=longint(longint(hEnde[Nr]-hStart[Nr])*360000+

             longint(mEnde[Nr]-mStart[Nr])*6000+

             longint(sEnde[Nr]-sStart[Nr])*100+

             hsEnde[Nr]-hsStart[Nr]);

{$Q+}

  dStr := txt(Zeitdauer div 100);

  if Zeitdauer mod 100 < 10 then

    ZeitStr := dStr + '.0'+txt(Zeitdauer mod 100) + ' sec'

  else

    ZeitStr := dStr + '.' +txt(Zeitdauer mod 100) + ' sec';

end;



function b : word;

begin

  b := Round( v*v*v* 0.96 );

end;



function a : word;

begin

  a := 100*v;

end;





begin

  v := 12;



  StartZeitMessung(1);

  for i := 1 to 1000000 do a;

  StopZeit(1);



  StartZeitmessung(2);

  for i := 1 to 1000000 do b;

  StopZeit(2);



  writeln( 'Zeitdauer 1. Funktion: ', ZeitStr(1) );

  writeln( 'Zeitdauer 2. Funktion: ', ZeitStr(2) );

  readln;



end.

Testplattform: 386sx16 (ohne Caches etc.)
Anzahl der Versuche: 20 (!)
Ergebnis:
Function a 15.05-15.16 sec
Function b 88.70-90.63 sec

Dabei war egal, ob function a vor function b definiert war. Es war auch egal, ob eine Funktion irgendwo anders, d.h. z.B. oberhalb der Hilfsprocedures wie StartZeitmessung o.ä. definiert war.

Ich hatte zwar am Anfang eine Häufung dafür, dass eine Konstellation schneller schien. Das hat sich aber nach 20 Versuchen gelegt. Ich hatte in jeder Konstellation mind. einmal das Extremum von 90.63 sec. Function a blieb im Übrigen ziemich konstant bei 15.15 sec. Die Werte 15.06 und 15.16 hatte ich nur je einmal.

Ergebnis: Gettime ruft m.W. eine Dosfunktion auf und ist ziemlich ungenau. Am Anfang hatte ich z.B. die Funktionsaufrufe um den Faktor 10 seltener aufgerufen. Da war dann teilweise Funktion b schneller als Funktion a, obwohl das nicht sein kann.

Benchmarking mache ich deswegen immer über den Timer-Interrupt, was auch nicht viel schwieriger ist. Aber hoffentlich ein bisschen genauer... GetTime verwendet man wohl eher, wenn man nur die aktuelle Uhrzeit (Stunde:Minute) wissen will, nicht für's Timing :-)
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

Wenn es rein an gettime liegen würde könnte sich mein Messergebnis auch mal drehen, das ist aber nicht der Fall.

Versuche das doch mal auf einem Pentium - auf meinem 486er waren die Messergebnisse beider Funktionen in etwa gleich - wegen "Diskettenlaufwerk will manchmal nicht funktionieren" (muss ich mal untersuchen) habe ich dort allerdings noch nicht mit "gedrehten" Funktionen gemessen.

Der Timerinterrupt hätte wieder das Problem das ich meine Laufvariable gegen Überläufe schützen muss z.b. per zweitvariable (was allerdings auch Zeit kostet und die Messergebnisse beeinflusst), die aktuellen Routinen machen auf meinem P120 2Mio Durchläufe in ~250ms da wären nach 4 Interrupts schon die Laufvariable voll.
freecrac
DOS-Guru
Beiträge: 861
Registriert: Mi 21. Apr 2010, 11:44
Wohnort: Hamburg Horn

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von freecrac »

Dosenware hat geschrieben:Ok, hatte übersehen das Bit 0 "genullt" wird - Die beschreibung in meinem Assemblerbuch klingt im ersten moment nach rcl.

was die Geschwindigkeit angeht: wie im Edit gesagt - je nachdem wie ich die Unterprogramme anordne ändert sich die Geschwindigkeit doch recht dramatisch, bis ich herausgefunden habe woran das liegt, bzw. wie ich das abstellen kann ist benchen nicht so ohne weiteres möglich.
Wie klein wäre denn so eine minimale Testroutine als executable?
Die Überlaufprüfung mit jc verbraucht vermutlich sehr viel Zeit. Es wäre wohl besser wenn wir nur Werte berechnen, die unser Ziel-Register nicht zum überlaufen bringen können.
Nur sind die Werte nicht im vornherein bekannt - ich werde wohl mal überprüfen bei welchen Eingangswerten dort der überlauf eintritt, evtl. kann ich den dann bereits vorher abfangen.
Unmittelbar vor jeder einzelnen Berechnung würde ich das Überprüfen auf Überlauf ungern machen wollen.
(...wenn es darum geht die Berechnung zu beschleunigen.)
Dann lieber in einem Duchgang nur Überprüfen und eine neue Tabelle mit Einträgen anlegen, deren Eingangswerte einen Überlauf erzeugen. Die alten Einträge davon können wir dann aus der alten Tab herausnehmen, so dass dort nur noch Werte übrig bleiben, die keinen Überlauf erzeugen. Diese Überprüfung am besten vor/ausserhalb jeder zeitkritischen Berechnung plazieren. Nun können die beiden Tabs mit den verschiedenen Eingangswerten auch mit unterschiedliechen Routinen/Verfahren in einem Durchgang jeweils berechenen lassen, ohne dass dort der Ablauf durch eine Überprüfungen auf Überlauf verzögert wird. Wenn man die Überprüfung aber nicht so auskoppeln kann, dann ist das alles aber wohl eher kontraproduktiv. Kommt ja immer ganz auf den Anwendungsfall an.

Dirk
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

Die Exe ist aktuell 5kb groß, der meiste code stammt wohl aus der Dos.tpu und der crt.tpu.

werde das ganze aber auf interrupts umstellen, die Testroutine die ich mir überlegt dürfte leicht erweiterbar und überlaufsicher sein.

pocedure interrupt
inc(i)
if i=anz then Fertigflag:=1;end;

...
call warten auf interrupt für korrekten start
@testschleife
call zu testende routine
inc variable_1
adc variable_2,0
...
adc variable_x,0
jc @error
cmp Fertigflag,1
jne @testschleife
#Fertig write(variable_summe, 'Durchläufe in ',anz,' ints');

PS das jc nach shl,3 kann raus, die überprüfung mache ich jetzt vor der schleife...
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von wobo »

Dosenware hat geschrieben:Wenn es rein an gettime liegen würde könnte sich mein Messergebnis auch mal drehen, das ist aber nicht der Fall.

Versuche das doch mal auf einem Pentium -...
Habe derzeit leider nur einen 386sx16 zur Hand. Aber vielleicht am WE, wenn das Problem dann noch aktuell ist;

Ich denke nach wie vor, dass GetTime der Schuldige ist. Ansonsten würde ich vielleicht auf Code Alignement raten: TP macht meiner Information nach kein Code Alignement. Vielleicht landet die zuerst definierte Procedure zufällig auf word/dword-Grenze, und die nachfolgende nur auf Byte-Grenzen.

Dosenware hat geschrieben: Der Timerinterrupt hätte wieder das Problem das ich meine Laufvariable gegen Überläufe schützen muss z.b. per zweitvariable (was allerdings auch Zeit kostet und die Messergebnisse beeinflusst), die aktuellen Routinen machen auf meinem P120 2Mio Durchläufe in ~250ms da wären nach 4 Interrupts schon die Laufvariable voll.
Also bei meinen Tests ging es ja im Kern immer darum zu prüfen, welche Variante in Relation zur anderen schneller ist. Für dieses Ergebnis ist es nicht relevant, ob der Timer-Int ein paar Instructions mehr ausführen muss. Die Relation bleibt ja für beide Varianten gleich. Ich weiß aber natürlich nicht, was Du benchen willst...
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

Ansonsten würde ich vielleicht auf Code Alignement raten: TP macht meiner Information nach kein Code Alignement.
Word Datenausrichtung ist in den Compileroptionen von BP7 aktiv

zum Timerint: siehe letzten Post, diese Testschleife sollte zur Laufzeit immer exakt gleichviele Zyklen benötigen und eine ungenauigkeit (bei idealem Timer und unter Vernachlässigung anderer Interrupts) von einem Durchauf besitzen.
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von wobo »

Dosenware hat geschrieben:
Ansonsten würde ich vielleicht auf Code Alignement raten: TP macht meiner Information nach kein Code Alignement.
Word Datenausrichtung ist in den Compileroptionen von BP7 aktiv
Code und Data-Alignment sind aber etwas unterschiedlliches. Bei Data-Alignment werden Deine Variablen im Speicher so angelegt, dass der Zugriff z.B. immer auf word-Grenzen erfolgt. Ein 16-bit-zugriff kann dann ideal schnell erfolgen.

Code-Alignment bedeutet, dass der Code, also die einzelnen Befehle an z.B. word - Grenzen ausgerichtet sind. Während ein Data-Alignment auch recht schnell von Hand gemacht ist, habe ich allerdings keine Ahnung, wie man ein Code-Alignment durchführen sollte. Das Problem ist vor allem, dass die x86-Befehle alle unterschiedlich lang sind, d.h. manche Befehle brauchen nur 1 byte, manche 5. Man müßte also wissen, wie lange ein Befehl braucht und dann entsprechend NOPs einfügen, denke ich mal.

Da Pascal lediglich ein Data-Alignment durchführen kann, aber kein Code-Alignement, könnte es m.E. eben sein, dass die erste procedure z.B. beginnend an einer Speicherstelle modulo 2 = 0 ausgeführt wird (word alignment) und ggf. zufällig die nächsten Befehle auch. (Zufällig, weil es davon abhängt, wie groß die nachfolgenden Befehle sind und inwieweit diese noch vom Alignment profitieren). Dann könnte es sein, dass die zweite Procedure von Pascal auf eine ungerade Speicherstelle gelegt wird und sich das zufällig negativ auf die nächsten Befehle auswirkt (je nachdem, welche Befehle folgen und welche Speichergröße die haben und ob die nur mittels Mis-Alignment aus dem Speicher geladen werden können). Dies könnte m.E. erklären, warum die Frage, an welcher Stelle eine procedure definiert ist, Auswirkungen auf die Verarbeitungsgeschwindigkeit haben könnte. Wenn der Schuldige nicht GetTime ist (;-)).

Ist aber, wie gesagt, nur wild geraten von mir. Und in solchen Fällen iiege ich meistens ordentlich daneben :-) .

Dosenware hat geschrieben: zum Timerint: siehe letzten Post, diese Testschleife sollte zur Laufzeit immer exakt gleichviele Zyklen benötigen und eine ungenauigkeit (bei idealem Timer und unter Vernachlässigung anderer Interrupts) von einem Durchauf besitzen.

... die Testroutine die ich mir überlegt dürfte leicht erweiterbar und überlaufsicher sein.

pocedure interrupt
inc(i)
if i=anz then Fertigflag:=1;end;

...
call warten auf interrupt für korrekten start
@testschleife
call zu testende routine
inc variable_1
adc variable_2,0
...
adc variable_x,0
..
Da müßte ein Fehler sein, wenn ich das richtig verstanden habe: inc setzt bei Überlauf m.W. kein Carry-Flag, d.h. Du müsstest "inc variable_1" in "add variable_1,1" ändern.

Edit: @wobo: Es heißt "alignment". "Alignement" ("allinjömoo") sagen nur die Franzosen.
Zuletzt geändert von wobo am Di 15. Mai 2012, 08:41, insgesamt 1-mal geändert.
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

ok, stimmt - die routine habe ich während der Arbeit schnell mal hingekritzelt

zur codeausrichtung: wenn der effekt wieder auftritt (bin ja grade nicht zuhause) werde ich an der gebremsten funktion mal ein 8bit Nop voranstellen - dann sollte das codealignment wieder stimmen.
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von wobo »

Dosenware hat geschrieben:ok, stimmt - die routine habe ich während der Arbeit schnell mal hingekritzelt

zur codeausrichtung: wenn der effekt wieder auftritt (bin ja grade nicht zuhause) werde ich an der gebremsten funktion mal ein 8bit Nop voranstellen - dann sollte das codealignment wieder stimmen.
Ich denke fast nicht, dass das langt. Zum einen könnte ja bereits der Procedure-Kopf und das Label, wohin der Call-Befehl führt misaligned sein (wenn es richtig ist, dass Pascal sich überhaupt nicht um ein alignment bzgl. des Codes kümmert, was meine Info ist). Dein NOP kommt dann zu spät. Außerdem führst Du das Programm ja auf einen Pentium aus. Theoretisch könnte daher die zuerst definierte Procedure auch von einem zufälligen dword alignment profitieren, was Du durch ein NOP nicht immer egalisieren kannst. Drittens kann ein NOP an der falschen Stelle ja nur verschlimmern, weil im schlimmsten Fall der Code einen Tick länger braucht...
Antworten