Autor Beitrag
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: 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 user profile iconGammatester:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
salsa20.pas:1774  x[ 0] := x[ 0]+x[ 4];   x[12] := x[12xor x[ 0];   x[12] := (x[12shl 16or (x[12shr (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:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
salsa20.pas:1776  a:= a + b;   d := d xor a;   d := (d shl 16or (d shr (32-16));
                                                      // esi=a, edx=b, eax=c, ebx=d
0051E7CF 89d7                     mov    %edx,%edi    // temp = b
0051E7D1 01f7                     add    %esi,%edi    // temp += a
0051E7D3 89fe                     mov    %edi,%esi    // a = temp
0051E7D5 31df                     xor    %ebx,%edi    // tmp ^= d
0051E7D7 c1c710                   rol    $0x10,%edi   // tmp <<< 16

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?:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
salsa20.pas:1776  inc(a, b);   d := a xor d;   d := (d shl 16or (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 ;)
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1652
Erhaltene Danke: 243

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Do 12.09.13 14:02 
Hallo,

so übel ist das vom Zeitverhalten doch nicht
ausblenden volle Höhe Delphi-Quelltext
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 16or (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
# [17For 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 16or (d shr (32-16));
  roll  $16,%ecx
  movl  %ecx,%eax
  testl  %esi,%esi
  jg  .Lj11
# [23end.
  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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Do 12.09.13 15:45 
Da hast du nochmal was anderes bekommen :roll:
[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."