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 »

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 versuche den Code auf 16 Byte auszurichten und für zeitkritische Routinen den Prefetch-Puffer mit einem Far jmp zu löschen.

Code: Alles auswählen

START:                 ; beliebige Befehle am Anfang der Anwendung; Offset-Adresse beginnt
                       ; z.B. bei 0100h, oder ein Vielfaches davon.

;----------------------
DB  0EAh               ; Prefetch-Puffer löschen:
DW  (OFFSET ACTION)    ; Die folgenden Zeilen erzeugen
DW  (SEG ACTION)       ; das Kommando JMP FAR CS:ACTION
;----------------------
 org START + ((($-START)/16)*16)+16  ; Code-Ausrichtung
;----------------------
ACTION:                ;Anfang der zeitkritischen Routine

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

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

hrm, dann werde ich wohl in Pascal jetzt Konstrukte verwenden ala:

unit blah;
Type x=Procedure(abc:Variable); {macht einen Zeiger}

implementation
Procedure xx(abc:Variable);asm;
15xnop
Befehle
end;
...
Init
Hole Adresse von xx überprüfe auf alignment und setze x entsprechend. - dann noch mit Codeverschiebung - ein Ret (und was Pascal da noch so rumbaut) sollte ja leicht zu erkennen sein bzw. wird ein eigener Rücksprung definiert + einen Markerwert, oder gibts noch eine Möglichkeit die Startadressen zu beeinflussen?


Edit: aber der reihe nach:
-Prüfen wo meine Routinen im Speicher liegen (ob da wirklich ein Missalignment eine Rolle spielt)
-Messen per Timeinterrupt
-Code durch den Speicher schieben (und messen ob und wieviel der Funktionsaufruf per Variable langsamer ist)
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:hrm, dann werde ich wohl in Pascal jetzt Konstrukte verwenden ala:

[...]
...
Hole Adresse von xx überprüfe auf alignment und setze x entsprechend. - dann noch mit Codeverschiebung - ein Ret (und was Pascal da noch so rumbaut) sollte ja leicht zu erkennen sein bzw. wird ein eigener Rücksprung definiert + einen Markerwert, oder gibts noch eine Möglichkeit die Startadressen zu beeinflussen? ...
Puh, das klingt ganz schön kompliziert. Ich komm´da nicht mehr mit, was Du vorhast. Ich schwitze nämlich schon immer, wenn ich von Pascal auf BASM wechsele.


Ich hatte jetzt mal meine Beispielsourcen auf einem 486dx4-100 laufen lassen. Ich hatte ca. 50 Durchläufe pro Kombination. Im Schnitt war die Procedure, die später definiert war, ca. 1% schneller als wenn sie früher (d.h. im Quelltext weiter oben) definiert war. Im Einzelfall hatte ich allerdings auch Aussetzer in die umgekehrte Richtung, aber die Häufung (wenn man da bei zweimal 50 Durchläufe sprechen kann) war schon auffällig.

Ein wenig ratlos bin ich schon, warum das so ist. Ich denke auch nicht, dass es unbedingt am Code Alignment liegt. Ich hatte auch ein paar Versuche mit einer procedure die ich zwischen die beiden (und später davor und danach) platziert hatte und die ich mit NOP(s) vergrößert hatte, in der Hoffnung bytegenau Einfluss auf das Alignment der nachfolgenden Procedure zu nehmen.

Auch hier haben sich - soweit ich das jetzt auf die Schnelle überblickt habe - die Ablaufzeiten der beiden Proceduren nicht groß verändert, was gegen meine Theorie des Code-Alignments spricht.
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

Manuelles Codealignment zur Laufzeit.

auf dem P120 waren die unterschiede der beiden UP rund Faktor 1,5 bis 2 - auf dem 486er eher gegen 0
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 »

wobo hat geschrieben:
Dosenware hat geschrieben:hrm, dann werde ich wohl in Pascal jetzt Konstrukte verwenden ala:

[...]
...
Hole Adresse von xx überprüfe auf alignment und setze x entsprechend. - dann noch mit Codeverschiebung - ein Ret (und was Pascal da noch so rumbaut) sollte ja leicht zu erkennen sein bzw. wird ein eigener Rücksprung definiert + einen Markerwert, oder gibts noch eine Möglichkeit die Startadressen zu beeinflussen? ...
Puh, das klingt ganz schön kompliziert. Ich komm´da nicht mehr mit, was Du vorhast.
Eine Adresse kann man doch teilen und wenn die Teilung nicht genau aufgeht und ein Rest übrig belibt, dann wissen wir wieviele Bytes der Adresse von unserem gewüschten Code-Alignment abweicht. Damit können wir nun auch bis zur nächsten Adresse noch vorne rechnen, die unserem gewünschten Alignment entspricht. Nun können wir die betreffenden Subroutine weiter nach vorne kopieren, so das der Anfang der Subroutine auf dieser nächsten Adresse liegt, die unserem gewünschten Alignment entspricht. Und falls ich das richtig verstanden habe, dann können wir nun die Subroutine an dieser neuen Adresse auch direkt anspringen.
Stimmt das so?

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 »

jepp, nachteile der Aktion sind:

-erhöhter Speicherverbrauch (15byte am Anfang + dem Marker am Ende)
-mglw. langsamere Sprungbefehle da die Zieladresse nicht mehr im Code sondern in einer Variable steht.

bei Zeitkritischen Sachen (Bildspeicher neu füllen innerhalb eines Frames, Scheduler) könnte das ganze dennoch von Vorteil sein.
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

Ich habe jetzt auf Ints umgestellt und jetzt wirds verrückt, der P120 und der K6/3 500 haben jetzt keine Unterschiede mehr zwischen beiden Routinen (0 Durchläufe unterschied bei 2514 bzw. 16796 Durchlaeufen und 200 Ticks (ca. 11s)), der 486er - der bei Gettime bei beiden Routinen keinen Unterschied hatte - hat jetzt einen konstanten Unterschied von 139 Durchläufen
(559/698). naja, mal schauen.

aktuell sieht die Routine so aus (musste ich 3mal neu schreiben Strg+F9+Dosbox/vergessen den Interruptzähler zurückzusetzen)

Code: Alles auswählen

uses crt,dos;
const Ticks=200;                                {Anzahl der Interrupts}
const startstop:word=1;                         {Startstopflag für die Messung}
const error:word=0;                             {Fehlerflag}

const intcount:word=0;                          {Zählvariable}
const lauf1:comp=0;                             {Laufvariablen}
const lauf2:comp=0;
var laufw1:array[0..3]of word absolute lauf1;
var laufw2:array[0..3]of word absolute lauf2;
var laufh0,laufh1,laufh2,laufh3:word;

var oInt:procedure;                             {alter Int}

(*#######################Insert Coin here#############################*)

procedure test1;var i:word;
 begin;for i:=0to 65535 do i:=i;end;

procedure test2;var i:word;
 begin;for i:=0to 65535 do i:=i;end;

(*/*)

procedure MInt;Interrupt;
 begin
 inc(intcount);
 if  intcount>=Ticks then startstop:=1;
 oInt;
 end;

procedure WaitForInt;Interrupt;
begin
 startstop:=0;
 oInt;
 SetIntVec($1C,@MInt);
end;

begin
laufh0:=0;laufh1:=0;laufh2:=0;laufh3:=0;
GetIntVec($1C,@oInt);
SetIntVec($1C,@WaitForInt);
repeat;until startstop=0;
repeat;
 asm
  add laufh0,1
  adc laufh1,0
  adc laufh2,0
  adc laufh3,0
  jnc @end
  mov error,1
 @end:
 end;
 (*Testroutine hier einsetzen*)
 test1;
until startstop=1;
SetIntVec($1C,@oInt);
laufw1[0]:=laufh0;laufw1[1]:=laufh1;laufw1[2]:=laufh2;laufw1[3]:=laufh3;
laufh0:=0;laufh1:=0;laufh2:=0;laufh3:=0;
intcount:=0;

SetIntVec($1C,@WaitForInt);
repeat;until startstop=0;
repeat;
 asm
  add laufh0,1
  adc laufh1,0
  adc laufh2,0
  adc laufh3,0
  jnc @end
  mov error,1
 @end:
 end;
 (*Testroutine hier einsetzen*)
 test2;
until startstop=1;
SetIntVec($1C,@oInt);
laufw2[0]:=laufh0;laufw2[1]:=laufh1;laufw2[2]:=laufh2;laufw2[3]:=laufh3;

if error=1 then writeln('ERROR, ueberlauf');
writeln('Ticks    : ',Ticks);
writeln('Routine 1: ',lauf1:0:0,'Durchlaeufe.');
writeln('Routine 2: ',lauf2:0:0,'Durchlaeufe.');
writeln;
if lauf1>lauf2 then
begin
 lauf1:=lauf1-lauf2;
 writeln('Routine 1 ',lauf1:0:0,' Durchlaeufe schneller.');
end
else
begin
 lauf1:=lauf2-lauf1;
 writeln('Routine 2 ',lauf1:0:0,' Durchläufe schneller.');
end;

sound(2000);
delay(2000);
nosound;
readln;
end.
Edit: lustigerweise ist die schnellere Routine missaligned und liegt auf Offset 28d - die langsame liegt auf Offset 0d

Edit2: Ich habe jetzt mal die Beiden Interrupte vorangestellt...und siehe da: alle Unterschiede weg (Offsets 98/126)
Hat jemand eine Idee was da passiert ist?

EDIT3: Cool, ich habe jetzt mal eine der beiden Testroutinen nur bis 65500, statt 65535, zählen lassen
Ergebnis: 486er kein Unterschied (745 Durchläufe), P120 1 Durchlauf Unterschied (2515/2514), K6/3 500 9 Durchläufe Unterschied (16805/16796) - Ich behaupte einfach mal das die Routine jetzt hinreichend genau arbeitet.

EDIT4: wenn ich wieder meine Ursprünglichen Routinen einsetze, sind wir wieder beim alten Problem.
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von wobo »

@Dosenware:

Ich hatte jetzt versucht, Deinen Source auf meinem 386sx16 laufen zu lassen, was nicht gelang, da der Datentyp comp (8 byte?) einen Coprozessor verlangt. Ich musste deswegen Deinen Source verändern. Dabei habe ich mich auf die Verwendung eines longint (4 byte) beschränkt, was bei den konkreten Testprocedure dicke langt (auf welchen Boliden testest Du denn, dass Du 8 byte große Laufvariablen benötigst?)

Außerdem habe ich in den letzten Zeilen noch die Adressen der Testprocedure (dezimal!) ausgeben lassen.

Hier der von mir für den 386sx16 angepasste Source:

Code: Alles auswählen

uses crt,dos;



const Ticks=200;                       {Anzahl der Interrupts}

const startstop:word=1;                {Startstopflag fuer die Messung}

const error:word=0;                    {Fehlerflag}



const intcount:word=0;                 {Zaehlvariable}



type tEasyPointer = record

       ofs,sgm : word;

     end;



var

    llauf1,

    llauf2 : longint;



    proc1, proc2 : pointer;



    oInt :procedure;                   {alter Int}



(*#######################Insert Coin here#############################*)



procedure test1;var i:word;

 begin;for i:=0 to 65535 do i:=i;end;



procedure test2;var i:word;

 begin;for i:=0 to 65535 do i:=i;end;



(*/*)



procedure MInt;Interrupt;

begin

  inc(intcount);

  if  intcount>=Ticks then startstop:=1;

  oInt;

end;



procedure WaitForInt;Interrupt;

begin

 startstop:=0;

 oInt;

 SetIntVec($1C,@MInt);

end;



begin



  llauf1 := 0;

  llauf2 := 0;



  GetIntVec($1C,@oInt);

  SetIntVec($1C,@WaitForInt);



  repeat;until startstop=0;

  repeat;

    asm

     db $66; add word ptr llauf1, 1

     jnc @end

      mov error,1

     @end:

   end;

   (*Testroutine hier einsetzen*)

   test1;

  until startstop=1;



  SetIntVec($1C,@oInt);



  intcount:=0;

  SetIntVec($1C,@WaitForInt);

  repeat;until startstop=0;

  repeat;

    asm

     db $66; add word ptr llauf2, 1

     jnc @end

      mov error,1

     @end:

    end;

    (*Testroutine hier einsetzen*)

    test2;

  until startstop=1;



  SetIntVec($1C,@oInt);



  if error=1 then writeln('ERROR, ueberlauf');

  writeln('Ticks    : ',Ticks);

  writeln('Routine 1: ',llauf1,' Durchlaeufe. ');

  writeln('Routine 2: ',llauf2,' Durchlaeufe. ');

  writeln;

  if llauf1>llauf2 then

  begin

    llauf1:=llauf1-llauf2;

    writeln('Routine 1 war ',llauf1,' Durchlaeufe schneller.');

  end

  else begin

    llauf1:=llauf2-llauf1;

    writeln('Routine 2 war ',llauf1,' Durchlaeufe schneller.');

  end;



  proc1 := @test1;

  writeln( tEasyPointer(proc1).sgm,':',tEasyPointer(proc1).ofs );

  proc2 := @test2;

  writeln( tEasyPointer(proc2).sgm,':',tEasyPointer(proc2).ofs );



  sound(2000); delay(200); nosound;

  writeln('Enter!');readln;

end.


Ich habe das dann auf einem i386sx16, einem i486dx4-100 und einem amd duron 750 laufen lassen:

i386sx16:
Routine test1: 56 Durchläufe (Offset 00)
Routine test2: 59 Durchläufe (Offset 38)
i486dx100:
Routine test1: 1397 Durchläufe (Offset 00)
Routine test2: 1397 Durchläufe (Offset 38)
amd duron 750:
Routine test1: 10268 Durchläufe (Offset 00)
Routine test2: 11713 Durchläufe (Offset 38)

Also Code Alignment schieße ich als Erklärung aus. Vielleicht springt der 386sx nur gerne an Offset=0. Vielleicht ist es dem 486 egal, wohin er springt. Vielleicht springt der duron gerne möglichst kurz. ?!?! Ich habe keine Ahnung.

Nur eines hat sich wieder bestätigt. Der 486dx100 ist heftig schneller als der 386sx16, hier ca. 23 Mal. Meine Erfahrung bisher war, dass mein alter 486dx40 eigentlich immer (d.h. auch in der praktischen Anwendung) mind. 8-10 Mal schneller als der 386sx16 ist (bei Ausführung von reinem 286er-/16 bit - Code), was ich jetzt durch den 486dx100 mochmal bestätigt sehe. Zwischen 386sx16 und 486dx40 waren jedenfalls immer Welten.

Mich würde mal interessieren, was mein Source auf Deinem P120 für Durchläufe-Zahlen auswirft (falls Du mal zuviel Zeit hasst ;-))

@Freecrac: Danke für die ausführliche, hilfreiche Erklärung. Anfangs konnte ich mir gar nicht vorstellen, dass man im Compilat der Heiligkeit (Turbo Pascal) so herumdoktern kann ;-). Aber so isser der Dosenware. Hat vor nix Angst ;-)
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

(auf welchen Boliden testest Du denn, dass Du 8 byte große Laufvariablen benötigst?)
siehe weiter oben, einiges von dem Zeugs das ich teste macht 2Mio Durchläufe in 250ms (da noch zu optimieren ist zwar rel. Sinnfrei aber who cares)- auf einem K6 III 500
Also Code Alignment schieße ich als Erklärung aus.
Ich ebenfalls, ich habe bereits Codealignment ausprobiert (mittels dummyproceduren) und dabei 2 Dinge festgestellt:
-Pascal schmeißt unbenutzte Unterprogramme aus dem Komplilat
-Das Codealignment ist unschuldig, ich habe dennoch mit identischen UPs verschiedene Laufzeiten...
Aber so isser der Dosenware. Hat vor nix Angst
ich habe etwas gefunden: Plug&Play für Isakarten selbst programmieren zu müssen *g*
Gutes gelingen, Brueggi ;-)

BTW: writeln(Ofs(blubb)); ;-)
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:
(auf welchen Boliden testest Du denn, dass Du 8 byte große Laufvariablen benötigst?)
siehe weiter oben, einiges von dem Zeugs das ich teste macht 2Mio Durchläufe in 250ms (da noch zu optimieren ist zwar rel. Sinnfrei aber who cares)- auf einem K6 III 500[7quote]
ah so.
BTW: writeln(Ofs(blubb)); ;-)
Gerade getestet. Funktioniert! Mir fällt übrigens auch schon die ganze Zeit auf, dass ich ständig type-caste... Bin wahrscheinlich schon sehr run-time-error-geschädigt.
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:
Aber so isser der Dosenware. Hat vor nix Angst
ich habe etwas gefunden: Plug&Play für Isakarten selbst programmieren zu müssen *g*
Gutes gelingen, Brueggi ;-)
Ah gut. Und ich dachte schon, ich wäre der einzige, der zu dem Thema sich keine Meinung traut ;-)

Gutes Gelingen, Brueggi! ;-)
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

Das PNP ist halt recht heftig, alleine die Identifikation der Karten...Boah 144 Lesezugriffe um EINE einzelne Karte herauszupicken WTF?

Gerade auf dem 486er getestet: und da sind sie wieder die 139 Durchläufe unterschied (698 max) - interessant ist dass wenn ich die beiden Interruptroutinen voranstelle die Geschwindigkeit allgemein höher ist (754 Durchläufe).

*g* mein K6 III/500 kann schneller nichts tun als dein Duron 750 - da wird wohl ein Loop verbaut worden sein (Die Paradedisziplin der K6 Prozessoren) - 167796 Durchläufe 0 unterschied

Der P120 macht immernoch seine 2514 Durchläufe.

Edit: und dein Db66 verärgert wohl den Debugger...
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:Das PNP ist halt recht heftig, alleine die Identifikation der Karten...Boah 144 Lesezugriffe um EINE einzelne Karte herauszupicken WTF?
PnP hatte ich zu aktiven Dos-Zeiten gar nicht mehr mitbekommen, da ich vorher ausgestiegen bin. Wollte mich jetzt mal einlesen und habe mit Eliandas Seite (Danke!) angefangen. Ich hatte dann ziemlich schnell für mmich definiert, dass ich das nicht können muss.
Dosenware hat geschrieben:
Also Code Alignment schieße ich als Erklärung aus.
Ich ebenfalls, ich habe bereits Codealignment ausprobiert (mittels dummyproceduren) und dabei 2 Dinge festgestellt:
-Pascal schmeißt unbenutzte Unterprogramme aus dem Komplilat
Sagt auch das Handbuch. Und ist das, was TP unter Source Code - Optimierung versteht ;-)
Dosenware hat geschrieben: -Das Codealignment ist unschuldig, ich habe dennoch mit identischen UPs verschiedene Laufzeiten...
dto.
Dosenware hat geschrieben: Gerade auf dem 486er getestet: und da sind sie wieder die 139 Durchläufe unterschied (698 max) - interessant ist dass wenn ich die beiden Interruptroutinen voranstelle die Geschwindigkeit allgemein höher ist (754 Durchläufe).
Erstaunlich. Ist auf meinem 386sx16 genauso. Befindet sich test1 an Offset 0 und test2 an Offset 38, dann schafft test1 56 Durchläufe und test2 59 (s.o.). Wenn ich jetzt die beiden proedures test1 und test2 nach den interrupt-Routinen definiere befinden sie sich an den Offsets 98 (test1) und 136 (test2). Und jetzt ist das Ergebnis umgekehrt: Jetzt schafft test1 59 Durchläufe und test2 nur noch 56!!! Die Reihenfolge test1 - test2 blieb stets gleich (siehe Offsets).
Dosenware hat geschrieben: *g* mein K6 III/500 kann schneller nichts tun als dein Duron 750 - da wird wohl ein Loop verbaut worden sein (Die Paradedisziplin der K6 Prozessoren) - 167796 Durchläufe 0 unterschied

Der P120 macht immernoch seine 2514 Durchläufe.

Edit: und dein Db66 verärgert wohl den Debugger...
K6: grins. Und dabei sind beide von amd... aber das ist halt so, wenn man low budget cpus kauft. Da fallen dann auch die Benchmark-Optimierungen weg.

db$66: War das fehlerhaft? zumindest beim K6 (167796 Durchläufe) scheint er ja den 32bit-override gefressen zu haben.
Benutzeravatar
Dosenware
DOS-Gott
Beiträge: 3745
Registriert: Mi 24. Mai 2006, 20:29

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von Dosenware »

nee, Td kann nur 286ercode

der K6 macht mit dem COde den ich gepostet habe auch 16796 Durchläufe, die 32Bit ändern also nichts

EDIT: "Da fallen dann auch die Benchmark-Optimierungen weg."
Afair waren die K6 Prozessoren die einzigen mit Loopoptimierung, ein P4 mit 2Ghz ist da ähnlich schnell - siehe auch: http://www.heise.de/ct/artikel/Prozesso ... 85254.html (ganz unten - Ich liebe diesen Artikel)
wobo
DOS-Guru
Beiträge: 613
Registriert: So 17. Okt 2010, 14:40

Re: Variablenbezüge in Assembler innerhalb Pascal

Beitrag von wobo »

Ich denke doch, dass die unterschiedlichen Laufzeiten ihre Ursache im Code Alignment haben.

Die Ausführungsgeschwindigkeiten hängen nämlich (zumindest indirekt, s.u.) vom Offset der zu testenden Procedure ab.

Die Werte stammen aus dem weiter oben von mir modifizierten Listing. Die Ausführungsgeschwindigkeiten beziehen sich immer auf die test1-procedure:

Code: Alles auswählen

Offset 	 i386sx16	     i486dx100
h0F8		        56		1397
h0F9 		62		1290
h0FA			59		1290
h0FB			58		1397
h0FC		56		1397
h0FD 		62		1397
h0FE			59		1397
h0FF			58		1397
h100			56		1397
h101			62		1397
h102			59		1397
h103			58		1397
h104			56		1397
h105			62		1397
h106			59		1397
h107			58		1397
h108			56		1397
h109			62		1290
h10A			59		1290
h10B			58		1397
Daraus ergibt sich meines Erachtens folgendes:

1.
Ursache der unterschiedlichen Laufzeiten ein und derselben Routine auf demselben PC ist das Code Alignment. Denn die Laufzeiten stehen in direkter Relation zu den Startoffsets der procedure, wie sich aus der Tabelle ergibt.

Irrelevant ist auch, ob und ggf. wieviele andere Procedures vorher oder nachher definiert sind (getestet!). Maßgeblich für die Auführungsgeschwindigkeit ist beim 386sx16 immer das MODULO 4 aus dem Offset der zu testenden Procedure, beim 486dx100 das MODULO 16. Bei identischen Moduli wiederholen sich die Ausführungsgeschwindigkeiten cpu-spezifisch.

2.
Dass die beste Ausführungsgeschwindigkeit nicht bei einem MODULO X=0 erzielt wird ist auch leicht erkärbar. Maßgeblich im engeren Sinn ist nämlich gar nicht der Startoffset der Procedure, da diese ja auch nur 56-62 bzw. 1290-1397 Mal in der Sekunde aufgerufen wird. Maßgeblich ist vielmehr der innere Loop der zu testenden procedure test1, der ja stets 65536 Mal pro Procedureaufruf erfolgt. Pro Sekunde sind das also 56-62 * 65536 (386sx16) bzw. 1290-1397 * 65536 Schleifendurchläufe. Es wäre also darauf zu achten, dass das Loop-Label der zu testenden Procedure code aligned wäre.

An welchem Offset die procedure selbst beginnt, ist also nur mittelbar bedeutsam, weil dies Einfluss auf das Loop-Label, das pro Aufruf 65536 Mal angesprungen wird, hat. (Bei meinem 386sx z.B. ist wohl ein Offset mit Modulo 4 = 1 ideal für das spätere Alignment der Loop-Schleife.)

3.
Hinsichtlich des Code-Alignments müßte man also "nur" darauf achten, dass die Schleifen aligned sind. Das halte ich aber für ein Ding der Unmöglichkeit: Wegen der unterschiedlichen Länge der Befehle müßte man auch das Alignment des aktuellen Befehls auf die nachfolgenden Befehle innerhalb der Schleife untersuchen. Je länger die Schleife, umso unmöglicher...

Außerdem verhält sich da wohl jeder Prozessor unterschiedlich. Während der 386sx16 sein bestes Alignment im konkreten Anwendungsfall bei Offset mod 16=9 hat, hat der 486dx hier sein worst-case-Szenario. Viel Spaß, wenn man auch noch den Pentium berücksichtigen will.

4.
Nicht, dass mir jetzt jemand drein redet ;-): Über das Ergebnis bin ich nämlich ziemlich froh. Ich bin eh kein Freund von Zyklen- oder Befehls(längen) zählen. Jetzt kann ich also wieder und weiter munter drauf los programmieren, ohne mir Gedanken über Befehlsoptimierungen zu machen. Hauptsache für mich ist eh' immer, dass mein code läuft, was ich schwer genug finde.

5.
Erstaunlich fand ich allerdings schon, dass der 386sx offensichtlich ein Alignment MODULO 4 bevorzugt, obwohl er sich die Befehle ja über einen 16-bit-Bus holen muss. Selbiges gilt für den 486dx, der offensichtlich auf Paragraphenausrichtung aus ist (trotz nur 32-bittigen Busses). Die Sache ist wohl noch viel komplizierter als befürchtet...

Edit: Leider verhauts mir immer das Layout in der Tabelle; der zweistellige Betrag ist die Zahl der Durchläufe auf dem 386sx, der vierstellige der vom 486dx100
Antworten