Autor |
Beitrag |
Martok
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: So 08.09.13 19:36
Aloha!
Mittlerweile mach ich immer mehr mit Freepascal und meistens ist das ja auch ganz toll. Manchmal allerdings bin ich vom generierten Code doch mittelmäßig schockiert, da oft Instruktionen erzeugt werden die so gar keinen Sinn machen und einfach rauszuoptimieren wären (LLVM hat einige der Sachen sogar als "sowas passiert uns nicht"-Musterbeispiele...)
Nehmen wir mal ein Stück aus der Hashfunktion der Salsa20-Implementation von Gammatester:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| salsa20.pas:1774 x[ 0] := x[ 0]+x[ 4]; x[12] := x[12] xor x[ 0]; x[12] := (x[12] shl 16) or (x[12] shr (32-16)); 0051E7B7 8b4dc0 mov -0x40(%ebp),%ecx 0051E7BA 8b45d0 mov -0x30(%ebp),%eax 0051E7BD 01c1 add %eax,%ecx 0051E7BF 894dc0 mov %ecx,-0x40(%ebp) 0051E7C2 8b45f0 mov -0x10(%ebp),%eax 0051E7C5 31c8 xor %ecx,%eax 0051E7C7 8945f0 mov %eax,-0x10(%ebp) 0051E7CA c1c010 rol $0x10,%eax 0051E7CD 8945f0 mov %eax,-0x10(%ebp) |
Schön, dass er das rol erkennt, aber der Rest ist offensichtlich besser möglich, wenn man den Speicherzugriff weglässt. Kann man ja selber machen, hilft aber auch nur begrenzt. Ohne {$OPTMIZATION RegVars} ändert sich nämlich gar nichts, mit erhält man immerhin das:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| salsa20.pas:1776 a:= a + b; d := d xor a; d := (d shl 16) or (d shr (32-16)); 0051E7CF 89d7 mov %edx,%edi 0051E7D1 01f7 add %esi,%edi 0051E7D3 89fe mov %edi,%esi 0051E7D5 31df xor %ebx,%edi 0051E7D7 c1c710 rol $0x10,%edi |
Danach ist edi belegt und die weiteren 4telrunden verwenden für die Addition ein LEA mit noch anderen temporären Variablen, die die Registerbelegung komplett durcheinander werfen und am Ende ist der Geschwindigkeitszuwachs weg. Oh, und es ist unlesbar, ich könnte jetzt nicht sagen wann edi weggeschrieben wird
Man kann das dann von Hand so hinschieben, dass es passt (Patch an upstream kommt nach diesem Thread...), aber eigentlich sollte ein halbwegs brauchbarer Compiler doch sowas selbst erkennen?:
Delphi-Quelltext 1: 2: 3: 4:
| salsa20.pas:1776 inc(a, b); d := a xor d; d := (d shl 16) or (d shr (32-16)); 0051E7CF 01d6 add %edx,%esi 0051E7D1 31f3 xor %esi,%ebx 0051E7D3 c1c310 rol $0x10,%ebx |
Meine Frage jetzt: warum ist das so? Selbst mein altes D7 hat nicht so schlechten Code generiert, und der war schon manchmal sehr... seltsam. Erträglich wird das mit der "vollen Breitseite" an Optimierungen, die ich mittlerweile per Include-File überall reinwerfe. Aber sind das überhaupt alle (laut Doku ja), und warum ist das notwendig? Compile-Zeit kostet es jedenfalls nicht signifikant, was doch eigentlich schonmal ein schlechtes Zeichen ist (tut der überhaupt was?). Generell geht es mir an der Stelle um Speed over Size. Festplatten sind billig
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| {$IfDef FPC} {$OPTIMIZATION ON} {$OPTIMIZATION LEVEL1} {$OPTIMIZATION LEVEL2} {$OPTIMIZATION LEVEL3} {$OPTIMIZATION REGVAR} {$OPTIMIZATION PEEPHOLE} {$OPTIMIZATION CSE} {$OPTIMIZATION ASMCSE} {$OPTIMIZATION DFA} {$endif} |
Grüße,
Martok
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Für diesen Beitrag haben gedankt: Boldar
|
|
Horst_H
Beiträge: 1652
Erhaltene Danke: 243
WIN10,PuppyLinux
FreePascal,Lazarus
|
Verfasst: Do 12.09.13 14:02
Hallo,
so übel ist das vom Zeitverhalten doch nicht
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63:
| {$IfDef FPC} {$OPTIMIZATION ON} {$OPTIMIZATION LEVEL3} {$OPTIMIZATION REGVAR} {$OPTIMIZATION CSE} {$OPTIMIZATION ASMCSE} {$OPTIMIZATION UNCERTAIN} {$CodeAlign loop=16} {$endif} var a,b,d,i : integer; begin randomize; a:= 08154711; d := -a; b := randseed; For i := 1000*1000*1000-1 downto 0 do begin a:= a + b; d := d xor a; d := (d shl 16) or (d shr (32-16)); end; end. asm # Var a located in register edx # Var b located in register ebx # Var d located in register eax # Var i located in register esi call FPC_INITIALIZEUNITS movl $0,%eax # [13] randomize; call SYSTEM_$$_RANDOMIZE # Var a located in register edx # [14] a:= 08154711; movl $8154711,%edx # [15] d := -a; movl %edx,%eax negl %eax # Var d located in register eax # Var b located in register ebx # [16] b := randseed; movl U_$SYSTEM_$$_RANDSEED,%ebx # Var i located in register esi # [17] For i := 1000*1000*1000-1 downto 0 do movl $999999999,%esi addl $1,%esi .balign 16,0x90 .Lj11: subl $1,%esi # [19] a:= a + b; movl %ebx,%ecx addl %edx,%ecx movl %ecx,%edx # [20] d := d xor a; xorl %eax,%ecx # [21] d := (d shl 16) or (d shr (32-16)); roll $16,%ecx movl %ecx,%eax testl %esi,%esi jg .Lj11 # [23] end. call FPC_DO_EXIT |
Dauert bei mit 0.95 Sekunden bei 3.2e9 Hz ( AMD Phenom II X955 ) sind das 3,04 Takte für 9 Befehle, übrigens für Ziel-CPU PentiumM optimiert.
Mit Version 2.6.0 sind es 1,5 Sekunden, weil ROL nicht genutzt wird.
Gruß Horst
|
|
Martok
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Do 12.09.13 15:45
Da hast du nochmal was anderes bekommen
[19] bei dir sieht aus wie ein LEA (%ebx,%edx,1),%edx in Einzelteilen...
Alles in allem sind das 32 solcher Operationen, jeweils in 4ergruppen in einer Schleife, da sind schonmal ein paar Register weg und kleine Unterschiede fallen auf.
Effektiv (1k Blöcke, inplace, encrypt only) war das nachher der Unterschied zwischen 17MB/s, 40MB/s und 60MB/s (FX6300 1.4GHz) in der Reihenfolge wie der Code im ersten Beitrag. Also schon signifikant.
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
|