Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Optimallösung gesucht: Little Endian <-> Big Endian (https://www.delphipraxis.net/113353-optimalloesung-gesucht-little-endian-big-endian.html)

FAlter 6. Mai 2008 16:58


Optimallösung gesucht: Little Endian <-> Big Endian
 
Hi,

kann man das noch optimieren? (Kommentiert ist der Code, den Delphi erzeugt.)

Delphi-Quellcode:
procedure SwapBytes(var X: WideChar);
asm
  mov  dx, [X] //mov dx, [eax]
  xchg dl, dh
  mov [X], dx //mov [eax], dx
end;

procedure SwapBytes(var X: UCS4Char);
asm
  mov  edx, [X] //mov edx, [eax]
  bswap edx
  mov  [X], edx //mov [eax], edx
end;
Und ich geh doch richtig davon aus, dass das, was übergeben wird, der Zeiger auf das Zeichen ist (wegen var)?

Mfg
FAlter

sirius 6. Mai 2008 17:25

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
1. x liegt in eax und du musst es nicht erst noch weiter herumschieben.
2. gibt es für 16bit noch die Delphi-Funktion swap. Die erzeugt direkt inline-Code.

himitsu 6. Mai 2008 17:39

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
@sirius: bei mir ist Swap 32 bit (Integer)
nja, aber es ist zumindestens noch eine Funktion ist
Delphi-Quellcode:
w := Swap(w);
// erzeugt aber das selbe Ergebnis wie
ByteSwap(w);
und als Inline ist es besser


aber nee, diesen Code kann man nicht mehr optimieren, (leider geht ja bei ASM kein Inline)
nur ein & könnte nicht schaden.
Delphi-Quellcode:
procedure SwapBytes(var X: WideChar);
asm
  mov  dx, [&X]
  xchg dl, dh
  mov  [&X], dx
end;

procedure SwapBytes(var X: UCS4Char);
asm
  mov  edx, [&X]
  bswap edx
  mov  [&X], edx
end;

// ohne BSWAP für alte CPUs
procedure SwapBytes(var X: UCS4Char);
asm
  mov  edx, [&X]
  xchr dh, dl
  rol  edx, 16
  xchg dh, dl
  mov  [&X], edx
end;
was du optimieren könntest, wäre mehrere Zeichen umzuwandeln und nicht nur Eines.



aber wie meinst du das?
Zitat:

Kommentiert ist der Code, den Delphi erzeugt.


[add]
@sirius:
Delphi-Quellcode:
function SwapBytes(X: WideChar): WideChar;
asm
  xchg al, ah
end;

function SwapBytes(X: UCS4Char): UCS4Char;
asm
  bswap eax
end;

Namenloser 6. Mai 2008 17:43

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Ich denke er meint den Code aus der CPU-Ansicht vom Debugger.

FAlter 6. Mai 2008 17:56

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Hi,

Swap kannte ich noch nicht, hab gleich mal nachgesehen.

Aus der Delphi-Hilfe:
X ist ein Ausdruck des Typs SmallInt oder Word (16 Bit).

Ich hab aber WideChar. Notfalls gehts mit Typecasten.

Aus der Delphi-Hilfe:
Die Funktion ist nur aus Gründen der Abwärtskompatibilität vorhanden.


Na Toll. Dann so:

Delphi-Quellcode:
procedure SwapBytes(var X: WideChar); inline;
//Hinweis siehe unter der Prozedur!
begin
  X := WideChar(Swap(Word(X)));
end;
{

Sollte CodeGear "swap" eines Tages endgültig abschaffen, bitte das hier nutzen:

procedure SwapBytes(var X: WideChar);
asm
  mov  dx, [X] //mov dx, [eax]
  xchg dl, dh
  mov [X], dx //mov [eax], dx
end;

}
Wenigstens wird der Aufruf jetzt so übersetzt (Result ist WideString):

Code:
[b]FAUnicode.pas.137: SwapBytes(Result[I]);[/b]
0045949D 8B06             mov eax,[esi]
0045949F 8D4458FE        lea eax,[eax+ebx*2-$02]
004594A3 0FB708           movzx ecx,[eax]
004594A6 86E9             xchg cl,ch
004594A8 668908           mov [eax],cx
004594AB 43               inc ebx
Schade, dass Inline und Inline-Assembler ( :lol: ) sich nicht vertragen.

@roter Kasten: Es steht zwar als Integer in der OH, aber es steht auch darunter, was ich oben geschrieben habe: Smallint oder Word, also 16 Bit.

Das & braucht man bloß, wenns nicht eindeutig ist. Es gibt aber kein Register X, also wird der Parameter genommen. Wenn ich ihn ch für Char genannt hätte, müsste ich &Ch schreiben.

Für mehrere Zeichen müsste ich mich noch genauer mit dem Aufbau von Strings und der Übergabe von String-Parametern beschäftigen, dann kann ich auch gleich den ganzen String umwandeln (und dyn. Arrays, denn USC4String ist ein type array of USC4Char). Da es jedoch nur geschrieben wurde, weil ich es schnell brauchte und ich eigentlich was ganz anderes tun wollte, werd ich mich im Moment nicht groß in was einlesen, was ich noch nicht kann und mit dem ursprünglichen Thema nichts zu tun hat. Hab aber schon die TODO-Kommentare gesetzt. ;) Evtl. hat ja hier jemand Langeweile.

Mit "wie es Delphi übersetzt" meine ich was der aus dem X macht. Erst hatte ich eax stehen aber das ging logischerweise nicht, da X als eax "übersetzt" wurde (der Parameter wurde also in eax übergeben). Aber das CPU-Fenster hat mir die Ursache gezeigt und ich bin auf (e)dx umgestiegen.

Das mit den Funktionen ist schon klar, dass die dann kürzer gehen (da ich dann keine Referenzen übergeben habe), aber das ist mehr Schreibarbeit. Wird es denn optimaler, wenn ich x := SwapBytes(x) schreibe? Kann sogar sein, wenn der Wert vorm Aufruf "zufällig" in (a)ax lag und später wieder dort gebraucht wird. Nur die Variante mit der obsolete-Funktion Swap ist so schon schnell genug, aber das als inline-Funktion zu schreiben macht auch keinen Unterschied.

Mfg
FAlter

himitsu 6. Mai 2008 18:20

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
[edit] ups, ausversehn gelöscht ... wollte eigentlich zitieren, statt editieren

FAlter 6. Mai 2008 18:44

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Hi,

nochmal zum Thema optimieren, indem ich den ganzen String auf einmal bearbeite (vielleicht mach ich es ja doch schon jetzt):

Delphi-Quellcode:
procedure SwapBytes(var X: WideString);
procedure SwapBytes(var X: UCS4String);
Wird da jetzt ein Zeiger auf einen WideString (UCS4String), welcher wiederum ein Zeiger auf die Wide(UCS4)Chars ist, übergeben?

Mfg
FAlter

PS: Welche alten CPUs unterstützen denn kein bswap? Ich dachte immer, das gibts ab i80386DX (und was vor dem i486DX ist, ist eh egal, da lässt sich ja kein Win32 drauf installieren).
PPS: Ein WideString kat keinen Referenzzähler? :shock:

[edit] Es heißt UCS4String - sorry. Dann ist der Fehler weiter unten wohl auch ursprünglich meiner. [/edit]

nicodex 6. Mai 2008 19:01

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Wenn die Delphi-Version Inline unterstützt, dann macht Assembler keinen Sinn mehr:
Delphi-Quellcode:
{$IFDEF BIG_ENDIAN}
type
  WordBE = Word;
{$ELSE ~BIG_ENDIAN}
function WordBE(AValue: Word): Word; inline;
begin
  Result := Swap(AValue);
end;
{$ENDIF BIG_ENDIAN}

procedure TForm20.FormCreate(Sender: TObject);
var
  Value: Word;
begin
  Value := WordBE($1234); // inlined: mov _x, $3412 !!!
  ShowMessage(IntToHex(Value, SizeOf(Value) * 2));
  Value := WordBE(Value); // inlined: xchg _l, _h
  ShowMessage(IntToHex(Value, SizeOf(Value) * 2));
end;

FAlter 6. Mai 2008 19:15

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Hi,

darum habe ich ja, wie in #5 geschrieben, die Funktion abgewandelt als inline mit swap. Das betrifft aber erstens noch nicht die 4-Byte-Variante und zweitens kann man evtl. noch was rausholen, wenn man die ganzen Strings umwandelt.

Mfg
FAlter

himitsu 6. Mai 2008 19:18

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
WideString+Ref: nein, denn Delphi leitet den Widestring an OleStr weiter und der kennt keine Refferenzzählung.

'ne "einfache" Lösung wäre:

Delphi-Quellcode:
procedure SwapString(Var S: WideString);
asm
  mov eax, [eax]

  test eax, eax
  jz  @exit

  mov ecx, [eax - 4]
  shr ecx, 1

  @loop:

  mov  dx, [eax]
  xchg dl, dh
  mov  [eax], dx

  add eax, 2
  dec ecx
  jnz @loop

  @exit:
end;

procedure SwapString(Var S: UCS4String);
asm
  mov eax, [eax]

  test eax, eax
  jz  @exit

  mov ecx, [eax - 4]

  @loop:

  mov  edx, [eax]
  bswap edx
  mov  [eax], edx

  add eax, 4
  dec ecx
  jnz @loop

  @exit:
end;

FAlter 6. Mai 2008 19:27

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Hi,

danke. Ein kleiner Fehler: Es heißt UCS4String statt USC4String (war plötzlich ne rote Wellenlinie da).

Wie ich an deinem Code erkennen kann, werden wirklich Zeiger auf die Zeiger übergeben. Aber jetzt ist die Frage ja nicht mehr nötig. Zumindest, wenn es läuft - werds gleich mal testen.

Mfg
FAlter

[edit] Scheint zu funktionieren - und ich versteh sogar was, ist also nicht zu kompliziert geworden! :D [/edit]

himitsu 6. Mai 2008 19:44

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
ups: nja, hatte die UCS4String-Version ohne zu testen (um)geschrieben :angel2:

PS: das "SHR 1" ist ein schnelles "div 2"
und es ist nötig, da die OleStr (ole32.dll) die Länge in Byte angibt.

und bei USC4 nicht, da Delphi bei seinen dynamischen Arrays (String, WideString, dynArray und Co.) die Anzahl der Elemente/Chars

PS: in FTypes.pas schau dir mal den Abschnitt "Compiler Intern Data-Types" (ab Zeile 1213) an
http://www.delphipraxis.net/internal...=879295#879295



bei "SwapString(Var S: WideString);" könnte man jetzt noch jeweils 2 Chars via BSWAP zusammen verarbeiten

FAlter 6. Mai 2008 19:57

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Hi,

Zitat:

Zitat von himitsu
PS: das "SHR 1" ist ein schnelles "div 2"

Ich weiß. Ich sag ja, ich versteh sie sogar :)

Zitat:

und es ist nötig, da die OleStr (ole32.dll) die Länge in Byte angibt.
Gut zu wissen.

Zitat:

PS: in FTypes.pas schau dir mal den Abschnitt "Compiler Intern Data-Types" (ab Zeile 1213) an
http://www.delphipraxis.net/internal...=879295#879295
Kann ich ja mal machen. :)


Zitat:

bei "SwapString(Var S: WideString);" könnte man jetzt noch jeweils 2 Chars via BSWAP zusammen verarbeiten
Macht BSwap nicht aus 01 02 | 03 04 ein 04 03 | 02 01? Wenn man da zwei 16-Bit-Werte zusammenfasst, sind die doch vertauscht? Ob man damit soviel besser kommt, danach nochmal die beiden tauschen zu müssen (zumal man nicht so ohne weiteres an die Bits 16..31 rankommt, wenns noch im e?x-Register steht)?

Mfg
FAlter

himitsu 6. Mai 2008 20:05

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
besser kommt man in dem Sinne, daß nun integerweise gearbeitet wird, also mit 2*16 Bit ... wobei die 32 Bit-CPU da doch optimaler arbeitet :angel:
Delphi-Quellcode:
procedure SwapString(Var S: WideString);
asm
  mov eax, [eax]

  test eax, eax
  jz  @exit

  mov ecx, [eax - 4]
  shr ecx, 1

  push ecx
  shr ecx, 1

  jz @onechar

  @loop:

  mov  edx, [eax]
  bswap edx
  rol  edx, 16
  mov  [eax], edx

  add eax, 4
  dec ecx
  jnz @loop

  @onechar:

  pop ecx
  and ecx, $01
  jz  @exit

  mov  dx, [eax]
  xchg dl, dh
  mov  [eax], dx

  @exit:
end;
ach, da ich eh grad etwas zuviel Zeit hatte (blöd, wenn man auf was warten muß :? )
ist aber noch ungetestet

FAlter 6. Mai 2008 20:18

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Hi,

stimmt, wenn man es rotieren lässt, hat man die beiden Teile des DWords ja auch wider getauscht, insofern ist ein effizientes Tauschen dann möglich.

Evtl. könnte man das Label @onechar noch drei Anweisungen nach unten verschieben, denn wenn es nur ein Zeichen ist muss nicht nochmal geprüft werden, ob die Anzahl ungerade ist (ein Byte übrig bleibt), das ist dann nämlich eben der Fall.

Mfg
FAlter

[edit] Getestet mit verschobenem @onechar läuft nicht. Was hab ich da nur schon wieder übersehen? :gruebel: [/edit]

nicodex 6. Mai 2008 20:38

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Mögliche Assembler-Version für UCS2-Swap:
Delphi-Quellcode:
procedure SwapWords(var AWords; ACount: LongWord); register;
asm
        mov    ecx, edx
        jecxz  @@exit
@@loop: ror    word ptr [eax + ecx * 2 - 2], 8
        loop @@loop
@@exit:
end;

procedure TestSwapWords();
var
  Value: WideString;
begin
  Value :=
    #$4800#$6500#$6C00#$6C00#$6F00#$2C00#$2000 +
    #$5700#$6F00#$7200#$6C00#$6400#$2100;
  SwapWords(Value[1], Length(Value));
  ShowMessage(Value);
end;

FAlter 6. Mai 2008 20:46

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Hi,

sieht zwar kurz aus, aber himitsus Version ist schöner, da sie (1.) die Parameter verwendet, wie ich sie mir vorstelle (mit viel weniger Tipparbeit) und (2.) die loop-Anweisung unschön ist. Keine Ahnung, wer die überhaupt eingeführt hat :? jedenfalls ist sie nur dann empfehlenswert, wenn man Speicherplatz sparen will, ansonsten kommt man mit dec und jz besser (zumindest wars früher so, evtl. ist sie heute etwas optimiert worden).

Mfg
FAlter

nicodex 6. Mai 2008 21:25

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Dann halt mit WideString und dec/jnz :)
Delphi-Quellcode:
procedure SwapUCS2(var AValue: WideString); register;
asm
        mov    eax, [AValue]
        or     eax, eax
        jz     @@exit         // Empty (nil)
        mov    ecx, [eax - 4] // Relies on internal BSTR knowlegde!
        shr    ecx, 1
//      jz     @@exit         // Should not happen (Empty <> nil)
@@loop: dec    ecx
        ror    word ptr [eax + ecx * 2], 8
        jnz    @@loop
@@exit:
end;

himitsu 6. Mai 2008 21:50

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
also ohne deine Signatur wär mir langsam was komisch vorgekommen (wo kommt denn das x-Anhänsel her?)

nja, gegen die Assemblerprofies hat man einfach keine Chance :|

dafür bin ich einfach zu sehr gedanklich in der Delphi-/Pascalschiene drin ... übersetze es sozusagen und versuch's dann zu optimieren...
Delphi-Quellcode:
procedure SwapString(Var S: WideString);
asm
  mov  eax, [eax]      // eax := PWideChar(S);
  test eax, eax        // if eax = nil then goto @exit;
  jz   @exit
  mov  ecx, [eax - 4]  // ecx = length(eax) {div 2};
  shr  ecx, 1
  push ecx             // temp := ecx;
  shr  ecx, 1           // ecx := ecx div 2;
  jz   @onechar        // if ecx = 0 then goto @onechar;
  @loop:                // @loop:
  mov  edx, [eax]      // PLongWord(eax)^ := bswap(PLongWord(eax)^) rol 16;
  bswap edx
  rol  edx, 16
  mov  [eax], edx
  add  eax, 4           // inc(eac, 4);
  //dec  ecx           // //dec(ecx);
  //jnz  @loo          // //if ecx <> 0 then goto @loop;
  loop @loop           // dec(ecx); if ecx <> 0 then goto @loop;
  @onechar:             // @onechar:
  pop  ecx             // ecx := temp;
  and  ecx, $01         // ecx := ecx and $01;
  jz   @exit           // if ecx = 0 then goto @exit;
  mov  dx, [eax]       // PWord(eax)^ := xchg(PWord(eax)^);
  xchg dl, dh
  mov  [eax], dx
  @exit:                // @exit:
end;
nja, zumindestens lerntman da auch immer was dazu.
wußte noch garnicht, daß man ROR/ROL nicht nur mit Register verwenden kann ...
schade, daß dieses nicht auch mit BSWAP geht, sonst würde ich brstimmt auch fast so weit runter(klein) kommen ^_^

nicodex 6. Mai 2008 22:38

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Wenn man davon ausgeht, dass UCS4String ein array of UCS4Char (dynamisches Array) ist, dann könnte man folgendes 'verbrechen':
Delphi-Quellcode:
procedure SwapUCS4(var AValue: UCS4String); register;
asm
        mov    eax, [AValue]
        or     eax, eax
        jz     @@exit         // Empty (nil)
        mov    ecx, [eax - 4] // Relies on internal DynArray knowlegde!
//      jecxz  @@exit         // Should not happen (Empty <> nil)
//      dec    ecx            // Skip the terminating $00000000
//      jz     @@exit
@@loop: dec    ecx
        mov    edx, [eax + ecx * 4]
        bswap  edx
        mov    [eax + ecx * 4], edx
        jnz    @@loop
@@exit:
end;

procedure TestSwapUCS4;
const
  Hello: array [0..13] of UCS4Char = (
    $48000000, $65000000, $6C000000, $6C000000, $6F000000, $2C000000, $20000000,
    $57000000, $6F000000, $72000000, $6C000000, $64000000, $21000000, $00000000
  );
var
  Value: UCS4String;
begin
  SetLength(Value, Length(Hello));
  Move(Hello[0], Value[0], SizeOf(Hello));
  SwapUCS4(Value);
  ShowMessage(UCS4StringToWideString(Value));
end;
Statt read/BSWAP/write hätte man an auch drei ROR (8/16/8) nehmen können:
Delphi-Quellcode:
procedure SwapUCS4(var AValue: UCS4String); register;
asm
        mov    eax, [AValue]
        or     eax, eax
        jz     @@exit
        sub    eax, 4
        mov    ecx, [eax]
//      dec    ecx // Skip terminating $00000000
//      jz     @@exit
@@loop: add    eax, 4
        dec    ecx
        ror    word ptr [eax], 8
        ror    dword ptr [eax], 16
        ror    word ptr [eax], 8
        jnz    @@loop
@@exit:
end;
ps: das x kommt von nicodex (Vorname) / nicodex (Thema) / nicodex (Land) / nicodex (Neigung) / nicodex (Spiel) :]

FAlter 7. Mai 2008 18:17

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Hi,

hm, jetzt muss ich mich nur noch für die beste Variante entscheiden, wo ich so viele Angebote habe. Aber drei ror auf den Speicher sind wohl bestimmt nicht so günstig wie zwei MOV mit Speicher und ein bswap mit Register.

Danke erstmal soweit. Und da wollte ich eigentlich ganz was anderes machen und das war nur so nebenbei. Naja, sollte ich mich nach genauer Prüfung für eine Variante entschieden haben, meld ich mich wieder.

Mfg
FAlter

nicodex 7. Mai 2008 19:56

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Ich habe mir keine weiteren Gedanken über andere Optimierungen gemacht... gibt sicherlich noch bessere Varianten. Im Zweifelsfall hilft immer: Performance messen :)

FAlter 8. Mai 2008 16:24

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Hi,

der Performancetest hat gezeigt, dass bei langen Strings, wie ich sie verwenden möchte, folgendes am schnellsten funktioniert:

Delphi-Quellcode:
procedure SwapBytes(Var S: WideString);
asm
  mov eax, [eax]

  test eax, eax
  jz  @exit

  mov ecx, [eax - 4]
  shr ecx, 1

  push ecx
  shr ecx, 1

  jz @onechar

  @loop:

  mov  edx, [eax]
  bswap edx
  rol  edx, 16
  mov  [eax], edx

  add eax, 4
  dec ecx
  jnz @loop

  @onechar:

  pop ecx
  test ecx, $01 //<-- hier habe ich ein test draus gemacht
  jz  @exit

  mov  dx, [eax]
  xchg dl, dh
  mov  [eax], dx

  @exit:
end;

procedure SwapBytes(Var S: UCS4String);
asm
  mov eax, [eax]

  test eax, eax
  jz  @exit

  mov ecx, [eax - 4]

  @loop:

  mov  edx, [eax]
  bswap edx
  mov  [eax], edx

  add eax, 4
  dec ecx
  jnz @loop

  @exit:
end;
Bei kurzen Strings spielt es kaum eine Rolle, welchen Code man nimmt. Somit scheint die Methode, in Pascal zu denken und dann zu übersetzen, gar nicht so falsch zu sein :)

Mfg
FAlter

[edit] Jetzt weiß ich, was ich in #15 übersehen habe: das pop :mrgreen: Kein Wunder, dass es ne AV gab, wenn die Rücksprungadresse nicht mehr stimmt. [/edit]

nicodex 9. Mai 2008 06:39

Re: Optimallösung gesucht: Little Endian <-> Big Endia
 
Gut zu wissen :)

Nur als Kommentar: Für lokale Labels verwendet man normalerweise @@.
Das einzelne @ braucht man z.B. für den Aufruf von System._WStrLen -> "call System.@WStrLen".


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:34 Uhr.

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