Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   [Assembler]: 2 Variabeln austauschen (https://www.delphipraxis.net/74916-%5Bassembler%5D-2-variabeln-austauschen.html)

St.Pauli 10. Aug 2006 20:11


[Assembler]: 2 Variabeln austauschen
 
Hallo,

mein Vorhaben scheint einfach - ist es für mich zumindestens nicht. :lol:
Ich wollte ein bisschen mit Inline-Assembler in Delphi rumspielen und eine Procedure schreiben,
welche mir meine 2 Variablen vertauscht.

Innerhalb meines Programmes klappt es wunderbar:

Delphi-Quellcode:
 
var
  a, b : longword;
begin
  a := 10;
  b := 20;
  asm
    MOV  EAX, a
    XCHG EAX, b
    MOV  a, EAX
  end;
Nur wie kann ich den ganzen Assembler-Teil jetzt in eine Procedure auslagern? Ich bekomme es einfach nicht gebacken...
Ich hoffe ihr könnt mir weiterhelfen...

JasonDX 10. Aug 2006 20:22

Re: [Assembler]: 2 Variabeln austauschen
 
Wenn du es in eine Prozedur auslagerst, musst du auf Delphis CallingConventions achten. D.h. es wird nicht wie beim stdcall alles auf den Stack gepusht, sondern zuerst wird eax, edx und ecx gefuellt.
D.h. mit diesem Prozedurkopf
Delphi-Quellcode:
procedure Switch(a, b: integer);
wird dir a in eax, und b in edx geschoben. Allerdings musst du die Parameter per Call by Reference uebergeben, da die veraenderung auch uebernommen werden soll, also so:
Delphi-Quellcode:
procedure Switch(var a, b: integer);
Das aendert nur so viel, dass nicht der Wert, sondern die Adressen der Variablen mitgegeben werden.
Zu deutsch wuerde deine Prozedur dann so aussehn:
Delphi-Quellcode:
procedure Switch(var a, b: integer);
asm
  mov ecx, [eax]
  xchg [edx], ecx
  mov [eax], ecx
end;
Du musst vorher den Wert von a ([eax]) in ecx schreiben, da du nicht 2 Parameter gleichzeitig dereferenzieren kannst.

greetz
Mike

St.Pauli 10. Aug 2006 20:36

Re: [Assembler]: 2 Variabeln austauschen
 
Es funktioniert perfekt! Danke für die schnelle Lösung und die ausführliche Beschreibung!!! :thumb:

Hawkeye219 10. Aug 2006 21:07

Re: [Assembler]: 2 Variabeln austauschen
 
Hallo St.Pauli,

hast du einmal getestet, ob die vorgestellte Assembler-Version wirklich schneller ist als eine reine PASCAL-Version? Die folgende Routine sollte etwas schneller sein:

Delphi-Quellcode:
procedure Switch (var a, b: integer);
asm
  push ebx
  mov ecx,[eax]
  mov ebx,[edx]
  mov [edx],ecx
  mov [eax],ebx
  pop ebx
end;
Eine reine PASCAL-Lösung ist übrigens etwa gleich schnell. Da du in deinem Profil "Delphi 2005" angibst, könntest du aber mit einer INLINE-Prozedur noch etwas Zeit einsparen:

Delphi-Quellcode:
procedure Switch (var a, b: Integer); inline;
var
  t : Integer;
begin
  t := a;
  a := b;
  b := t;
end;
Gruß Hawkeye

JasonDX 10. Aug 2006 21:30

Re: [Assembler]: 2 Variabeln austauschen
 
Zitat:

Zitat von Hawkeye219
Die folgende Routine sollte etwas schneller sein:

Delphi-Quellcode:
procedure Switch (var a, b: integer);
asm
  push ebx
  mov ecx,[eax]
  mov ebx,[edx]
  mov [edx],ecx
  mov [eax],ebx
  pop ebx
end;

Es geht noch schneller ^^
Delphi-Quellcode:
procedure Switch (var a, b: integer);
asm
  push [eax]
  mov ecx, [edx]
  mov [eax], ecx
  pop [edx]
end;
Aber ich glaube, die Geschwindigkeitsunterschiede sind hier sehr gering, sodass sie kaum ins Gewicht fallen ;)

greetz
Mike

St.Pauli 10. Aug 2006 21:39

Re: [Assembler]: 2 Variabeln austauschen
 
OK, ich habe mal alle 3 Proceduren getestet:
  • JasonDX: ~140 ms
  • Hawkeye219 in Assembler: ~46 ms
  • Hawkeye219 ohne Assembler: ~15 ms
Tatsächlich ist die Inline-Procedure am schnellsten!!! Aber Jason hat recht. Um überhaupt eine Differenz zu bekommen musste ich enorm viele Durchläufe durchführen, was den Zeitvorteil zunichte macht, da ich die Procedure nur ein paar Mal aufrufen muss...

Egal, jetzt habe ich die Qual der Wahl zwischen den 3 Versionen :mrgreen:

JasonDX 10. Aug 2006 21:49

Re: [Assembler]: 2 Variabeln austauschen
 
Zitat:

Zitat von St.Pauli
OK, ich habe mal alle 3 Proceduren getestet:
  • JasonDX: ~140 ms
  • Hawkeye219 in Assembler: ~46 ms
  • Hawkeye219 ohne Assembler: ~15 ms

Ich schaetze, du hast die obere meiner Funktionen genommen (d.h. nicht die aus meinem 2. Beitrag im Thread), denn ansonsten wuerde mich das Ergebnis wundern ^^

Zitat:

Zitat von St.Pauli
Tatsächlich ist die Inline-Procedure am schnellsten!!!

Das war zu erwarten. ;) Bei einem Prozeduraufruf geschieht ziemlich viel, man sehe sich das CPU-Debugfenster an ;)
Eine Inline-Function erspart sich die ganze Pusherei auf den Stack, was einen enormen Geschwindigkeitsschub gibt. :)
Interessant waere noch, was eine asm-inline-Funktion ergeben wuerde. Ich hab derzeit leider nur D7 drauf, da gibts noch kein inline :(

greetz
Mike

Dax 10. Aug 2006 21:50

Re: [Assembler]: 2 Variabeln austauschen
 
In Inline-Methoden darf man keine Assembler-Befehle benutzen. Hab ich früher mal schmerzlich gemerkt, das Inline weggenommen, Assemblerbefehle reingebaut und mich gefreut, das es am Ende doch schneller war ;)

Hawkeye219 10. Aug 2006 22:01

Re: [Assembler]: 2 Variabeln austauschen
 
Zitat:

Zitat von JasonDX
Zitat:

Zitat von St.Pauli
Tatsächlich ist die Inline-Procedure am schnellsten!!!

Das war zu erwarten. ;) Bei einem Prozeduraufruf geschieht ziemlich viel, man sehe sich das CPU-Debugfenster an ;)
Eine Inline-Function erspart sich die ganze Pusherei auf den Stack, was einen enormen Geschwindigkeitsschub gibt.

Genau darum ging es mir eigentlich. Ich habe in der Vergangenheit auch häufiger versucht, durch Assemblerroutinen einen Geschwindigkeitsvorteil zu erlangen, und bin dabei in einige Fallen getappt (z.B. die extrem langsame XCHG-Anweisung bei Speicheroperanden). Seitdem es INLINE gibt, trete ich nur noch äußerst selten gegen den Compiler an...

Gruß Hawkeye

Namenloser 11. Aug 2006 00:18

Re: [Assembler]: 2 Variabeln austauschen
 
Öhm, was bewirkt eigentlich Inline? Kann man das bei jeder Methode verwenden? Bringt es bei jeder Methode inen Geschwindigkeits-Boost? Achgottchen, das wär ja schön :stupid: :drunken:

jbg 11. Aug 2006 00:42

Re: [Assembler]: 2 Variabeln austauschen
 
Zitat:

Zitat von NamenLozer
Öhm, was bewirkt eigentlich Inline?

Es bewirkt, dass der Compiler statt eines Funktionsaufrufes gleich die ganze Funktion an der Aufrufstelle einfügt.

Zitat:

Kann man das bei jeder Methode verwenden?
Können schon, nur ist der Compiler etwas wählerisch. Und wenn man alles als Inline deklariert, hast du am Schluss (theoretisch) Code produziert, bei dem jeder Funktion zig tausendfach vorhanden ist. Über die Exe-Dateigröße will ich da erst gar nicht nachdenken. Das aber nur theoretisch, weil der Compiler nicht alles inlinen kann/will.

Zitat:

Bringt es bei jeder Methode inen Geschwindigkeits-Boost?
Nein. Es kann sogar den gegenteiligen Effekt haben, weil dem Register-Allocator die freien Register ausgehen und der Compiler dann Code generiert der nur noch mit push und pop beschäftigt ist. Ruft man hingegen eine Funktion auf, so stehen dem Register-Allocator wieder alle Register zur Verfügung.

Hier mal ein Auszug aus meinem Code aus den Indy-Komponentnen (IdGlobal.pas). Würden die lokalen Find() und FindNext() ge-inlined wäre der Code um ein ganzes Stück langsamer, weil der Compiler das ESI Register im Inline-Code nicht verweden kann und somit bei jedem Schleifendurchlauf den PChar-Zeiger neu aus dem Speicher einliest und danach wieder dorthin schreibt (2 Speicherzugriffe mehr pro Schleifendurchlauf).
Delphi-Quellcode:
function PosIdx(const ASubStr, AStr: AnsiString; AStartPos: Cardinal): Cardinal;

  // use best register allocation on Win32
  function Find(AStartPos, EndPos: Cardinal; StartChar: AnsiChar; const AStr: AnsiString): Cardinal;
  begin
    for Result := AStartPos to EndPos do
      if AStr[Result] = StartChar then
        Exit;
    Result := 0;
  end;

  // use best register allocation on Win32
  function FindNext(AStartPos, EndPos: Cardinal; const AStr, ASubStr: AnsiString): Cardinal;
  begin
    for Result := AStartPos + 1 to EndPos do
      if AStr[Result] <> ASubStr[Result - AStartPos + 1] then
        Exit;
    Result := 0;
  end;

var
  StartChar: AnsiChar;
  LenSubStr, LenStr: Cardinal;
  EndPos: Cardinal;
begin
  if AStartPos = 0 then
    AStartPos := 1;
  Result := 0;
  LenSubStr := Length(ASubStr);
  LenStr := Length(AStr);
  if (LenSubStr = 0) or (AStr = '') or (LenSubStr > LenStr - (AStartPos - 1)) then
    Exit;

  StartChar := ASubStr[1];
  EndPos := LenStr - LenSubStr + 1;
  if LenSubStr = 1 then
    Result := Find(AStartPos, EndPos, StartChar, AStr)
  else
  begin
    repeat
      Result := Find(AStartPos, EndPos, StartChar, AStr);
      if Result = 0 then
        Break;
      AStartPos := Result;
      Result := FindNext(Result, AStartPos + LenSubStr - 1, AStr, ASubStr);
      if Result = 0 then
      begin
        Result := AStartPos;
        Exit;
      end
      else
        Inc(AStartPos);
    until False;
  end;
end;

Namenloser 11. Aug 2006 13:37

Re: [Assembler]: 2 Variabeln austauschen
 
Vielen dank :-D


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:33 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz