Entwickler-Ecke

Programmierwerkzeuge - Compilerschalter ! Programm beschleunigen ?


Hochhaus - Di 17.12.13 10:49
Titel: Compilerschalter ! Programm beschleunigen ?
Hallo allerseits !

Ich habe eine mathematische Anwendung programmiert. Nun meine Frage: Mit welchen Compilerschaltern kann ich bewirken, dass mein Programm schneller ausgeführt wird ?

Danke im Voraus für jede Antwort !


Hochhaus


Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Di 17.12.2013 um 10:22


baumina - Di 17.12.13 10:59

- Bereichs- und Überlaufprüfung : FALSE
- Optimierung : TRUE
- Vollständige Boolesche Auswertung : FALSE (außer du brauchst das unbedingt)


Hochhaus - Di 17.12.13 11:01

user profile iconbaumina hat folgendes geschrieben Zum zitierten Posting springen:
- Bereichs- und Überlaufprüfung : FALSE
- Optimierung : TRUE
- Vollständige Boolesche Auswertung : FALSE (außer du brauchst das unbedingt)


Wie lauten diese Schalter ? {$L-} ?

Hochhaus


baumina - Di 17.12.13 11:17

Unter Projekt/Optionen/Compilieren müssten die Schalter genau so heißen, wie ich es geschrieben habe. Zudem gibt's dort die F1-Taste, die einem dazu genaueres sagt.


jfheins - Di 17.12.13 12:14

Ich bin mal gespannt ob das Programm damit merklich/messbar schneller wird. Falls nicht bietet es sich an, einen Profiler zu benutzen.


Hochhaus - Di 17.12.13 12:19

Es ist nicht schneller geworden, sondern ziemlich genau gleich geblieben ...


Hochhaus

P.S. Mit einer älteren Delphi-Version (6 PE) ist es aber deutlich schneller.


jfheins - Di 17.12.13 14:13

In dem Fall wie gesagt profilen und vielleicht mal den kritischen Code hier posten. (Welcher Code kritisch ist, bekommt man ja gerade mit einem Profiler heraus)


Martok - Di 17.12.13 14:43

Es gibt keinen magischen Mach-das-Programm-schnell-Schalter.

user profile iconHochhaus hat folgendes geschrieben Zum zitierten Posting springen:
Mit einer älteren Delphi-Version (6 PE) ist es aber deutlich schneller.
Dann ist dein Code noch nicht gut. Sollte es nur noch am mathematischen hängen, sind aktuelle Delphis doch schon ganz ordentlich, jedenfalls ähnlich FPC mit allen Optimierungen (user profile iconHorst_H kann da mehr sagen). Kommt noch lange nicht an äquivalenten Code in GCC oder clang ran, aber das wird dieses Jahrzehnt vielleicht noch was. Spätestens wenn Embadingsda keine Lust mehr hat und endlich das verdammte LLVM-Frontend schreibt. Anyway, ich schweife ab :lol:

Ja, wie schon geschrieben, du solltest zuerst deinen Code vermessen und feststellen, wo die Problemstellen sind. Ich benutze da auch immer gerne AMDs CodeAnalyst (heißt jetzt anders, die neuen laufen aber nicht mehr unter XP. Ja, ich habe mir deswegen meine CPU-Definition selbst gebaut.), der gibt dir ohne Instrumentierung schonmal eine Analyse auf Instruktionsebene. Wenn du noch die PDB für dein Programm hast (user profile iconjaenicke hatte da neulich was verlinkt), bekommst du sogar direkt Zeilennummern, ansonsten macht sich da die Erfahrung bezahlt, seinen Code auch im Assembler wiederzuerkennen ;)

Dann weißt du, wo das Problem ist, und kannst erstmal prüfen was man schneller machen kann. In den allermeisten Fällen wird das ein Speicherzugriff sein, denn, festhalten - RAM ist langsam. Jedenfalls gegenüber dem L1-Cache der CPU :zwinker:

HP-Optimierung ist ein Handwerk. Intel behauptet gerne anderes, aber auch ihr Compiler vektorisiert nur das, was vorher schon vektorisierbar geschrieben wurde.


Hochhaus - Di 17.12.13 16:43

Hallo !

hier die zeitkritische Schleife in meinem Proggi:


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:
  While (I <= N) And Go Do Begin

     Application.ProcessMessages;
     If (I Mod 100 = 0Then Begin
       ZeitY := GetTickCount - ZeitX;
       ZeitReal := ZeitY / 1000.0;
       ZeitR.Text := FloatToStrF(ZeitReal, ffFixed, 63) + ' sec.';
       Str(I, AnzStel);
       StelleX.Text := AnzStel;
     End;

     Stop.Enabled := True;
     Clear.Enabled := False;
     {* Ausmultiplizieren in Vektorschreibweise *}

     For W := -1 To I Do Begin
         Inc(K[W + I], 2 * A[W]);
     End;  {* For *}

     K[2 * I] := K[2 * I] + 1;


     {* Stellenübertrag *}

     For W := 2 * I DownTo -1 Do Begin
         Z := 0;
         While (K[W] > 9Do Begin
           Dec(K[W], 10);
           Inc(Z);
         End;
         Inc(K[W - 1], Z);
     End;  {* For *}


     {* 10'000 fache Eingabe und 10'000 fache Multiplikationssumme *}

     Summe1 := Round(1.0E06 * K[-2] + 1.0E05 * K[-1] + 1.0E04 * K[0] +
               1000.0 * K[1]);
     Summe1 := Summe1 + Round(100.0 * K[2] + 10.0 * K[3] + 1.0 * K[4]);
     Summe2 := Round(10000.0 * ZAHL);


     {* Grösse der Eingabe erreicht / Oder letzte Ziffer = 9 *}

     If (Summe1 >= Summe2) Or (A[I] = 9Then Begin
        Inc(I);
        For W := -2 To 2 * I Do Begin
            K[W] := S[W];
        End{* For *}
     End {* If (Summe1 ... Then *}


     {* Sonst: Letzte Ziffer um 1 erhöhen *}

     Else Begin
        Inc(A[I]);
        For W := -2 To 2 * I Do Begin
            S[W] := K[W];
        End{* For *}

     End;  {* IF (Summe1 ... Else *}
  End;  {* While I <= N *}


In der Beilage ist das gesamte Proggi als Zip-Datei ...!


Grüsse


Hochhaus


Gammatester - Di 17.12.13 17:05

Vielleicht ist es ja ein dumme Frage, aber: Was hat Application.ProcessMessages in der zeitkritische Schleife zu suchen, bzw warum packst Du es nicht in den Teil If (I Mod 100 = 0) Then?


Hochhaus - Di 17.12.13 17:22

Weil bei EinKernProzessor-Systemen der Prozessor fast zu 100 % ausgelastet wird - und dann keine Zeit mehr für andere Tasks bleibt. Jedes Mal, wenn eine neue Stelle berechnet wird, steht gegebenfalls auch Zeit für andere Aufgaben an. Sonst "friert der PC ein".

Hochhaus

P.S. Dies ist nicht der Grund für das langsamere Rechnen. Unter Delphi 6 PE war der Code derselbe und das Proggi war schneller. Das Auskommentieren der Zeile bringt praktisch keinen Zeitgewinn.


jaenicke - Di 17.12.13 22:54

Auf den ersten Blick würde ich vermuten, dass die Arrayzugriffe erheblich Potential zur Optimierung bieten. ;-)

Delphi-Quelltext
1:
2:
3:
     For W := -1 To I Do Begin
         Inc(K[W + I], 2 * A[W]);
     End;  {* For *}
Das ist ein extremer Zeitfresser. Du gehst eigentlich den Speicher byteweise durch und erhöhst die einzelnen Bytes. Trotzdem wird jedesmal aus der Summe aus w + i und der Adresse des Arrays per Multiplikation die Adresse neu berechnet. Dass dauert.

// EDIT:
Alleine durch das Austauschen von:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
     If (Summe1 >= Summe2) Or (A[I] = 9Then Begin
        Inc(I);
        For W := -2 To 2 * I Do Begin
            K[W] := S[W];
        End{* For *}
     End {* If (Summe1 ... Then *}


     {* Sonst: Letzte Ziffer um 1 erhöhen *}

     Else Begin
        Inc(A[I]);
        For W := -2 To 2 * I Do Begin
            S[W] := K[W];
        End{* For *}

     End;  {* IF (Summe1 ... Else *}
gegen dies:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
     if (Summe1 >= Summe2) or (A[I] = 9then
     begin
        Inc(I);
        CopyMemory(@K[-2], @S[-2], 2 * I + 3);
     end {* If (Summe1 ... Then *}
     {* Sonst: Letzte Ziffer um 1 erhöhen *}
     else
     begin
        Inc(A[I]);
        CopyMemory(@S[-2], @K[-2], 2 * I + 3);
     end;  {* IF (Summe1 ... Else *}
ist das ganze bei 10000 Nachkommastellen von 7,6 Sekunden auf 5,6 runter.


jfheins - Di 17.12.13 23:55

Ich würde das Array auch noch deutlich komprimieren. Momentan werden ja in einzelnen Bytes Dezimalziffern gespeichert. Das heißt die CPU muss sich zuerst die Bytes heraus picken, dann rechnen und braucht schnell wieder neue Daten.
Ein Byte kann ja eigentlich zwei Ziffern halten - Zahlen zwischen 0 und 99. Das hat mir damals auch geholfen, als ich dieses "Basis 26 Programm" im Thread von Mathematiker gemacht habe. Da habe ich sogar int64 genommen.
Die Maßnahme erhöht die Lokalität der Daten (sagt man das so?) so dass mehr relevante Daten in den CPU Cache passen und seltener aus dem RAM nachgeladen wird.

Ach, und parallelisieren kann man da auch ne Menge. Einerseits natürlich die großen Array-Arbeiten, und andererseits auch im kleinen mit SIMD (SSE lässt grüßen).


Hochhaus - Mi 18.12.13 07:24

Vielen Dank für die Hilfe !!

Hochhaus


Hochhaus - Do 19.12.13 16:00

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

ist das ganze bei 10000 Nachkommastellen von 7,6 Sekunden auf 5,6 runter.


Das stimmt unter Delphi XE2 ff. Unter Delphi 6 PE wird offenbar von sich aus optimiert: Dort ist Dein Code genauso schnell wie mein ursprünglicher Code ...!

Hochhaus


Blup - Fr 20.12.13 12:53

ProzessMessages und die Zeitausgabe haben auch auf einem Einkernprozessor nichts in einer zeitkritischen Berechnungsschleife zu suchen.
Für die Berechnung startet man einen extra Thread, um die Verteilung der Rechenzeit kümmert sich das Betriebsystem.
Der Hauptthread kann dann gemütlich ProzessMessages aufrufen und den Bildschirm aktualisieren, ab und zu nach dem Status des Berechnungsthreads schaun und sich ansonsten schlafen legen.


jaenicke - Fr 20.12.13 14:28

Das ist hier aber trotzdem nicht der Grund warum es so lange dauert. Das ändert kaum etwas.


Hochhaus - Fr 20.12.13 15:07

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Das ist hier aber trotzdem nicht der Grund warum es so lange dauert. Das ändert kaum etwas.


So ist es. Man kann ja den fraglichen Code auch auskommentieren, und dann schauen, ob es schneller geht ! Die Zeitfresser sind eindeutig bei den aufwändigen Array-Rechnungen zu suchen. Da ich eine Brute-Force-Variante des Wurzelziehens wählte, ist der Rechenaufwand groß. Es gäbe sicher bessere, intelligentere Methoden ...

Hochhaus


OlafSt - Sa 21.12.13 01:17

Es ist doch auch möglich, das der Compiler "Schuld" daran ist. Ist es denn so abwegig, das man die Routinen für Arrayzugriffe in laufe der Jahre verändert hat, so das *heute* gebräuchliche Zugriffsmethoden nun schneller sind, diese gezeigte Form aber nun langsamer ? Oder das die in D6 noch handoptimierten Assembler-Routinen wegen der 64-Bit-Compilergeschichten nun durch langsameren Pascalcode ersetzt wurden ? Auch am Optimizer kann herumgebastelt worden sein und und und...

Der Möglichkeiten gibt es viele und ein heutiges Delphi-Compilat ist auch nicht mehr der Weisheit letzter Schluß. Wenn ein C#-Programm, das erst noch JIT übersetzt werden muß, schneller läuft als die Delphi-Version (D2010, mit $O+, $R- und ohne jedwede DebugInfo übersetzt) des gleichen Algorithmus, dann ist da was nicht ganz optimal. Leider hab ich die beiden Progrämmchen nicht mehr, sonst würde ich es demonstrieren.


Hochhaus - Sa 21.12.13 02:17

user profile iconOlafSt hat folgendes geschrieben Zum zitierten Posting springen:
Es ist doch auch möglich, das der Compiler "Schuld" daran ist.


Ja, das vermute ich.

Hochhaus


jaenicke - Sa 21.12.13 11:28

Ein schneller Blick zeigt, dass der generierte Assemblercode nur an einer Stelle abweicht. Es wird eine Variable auf den Stack gelegt statt sie direkt in ein Register zu packen wie es bei Delphi 6 der Fall war. Allerdings wurde bei Delphi 6 esi genutzt, was eigentlich nicht unbedingt für normale Variablen gedacht ist.


Hochhaus - Sa 21.12.13 12:33

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Allerdings wurde bei Delphi 6 esi genutzt


Eine dumme Frage: Was ist esi ?


Hochhaus


Th69 - Sa 21.12.13 13:02

Hallo,

ESI steht für Extended Source Index und ist ein spezielles Register, s.a. x86 Registers [http://www.eecg.toronto.edu/~amza/www.mindsec.com/files/x86regs.html].