![]() |
Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Moinsen Leute,
ich habe versucht ein Programm mit Lazarus zu schreiben, welches Bilder auf Ähnlichkeit prüft, nur um dann festzustellen, dass mein Programm extrem langsam ist und selbst nach meinen Optimierungsversuchen es nicht wirklich besser wurde, weshalb ich auf eine vorhandene Bibliothek (OpenCV) versuche, zurzückzugreifen. Mit der Installation habe ich meine Probleme und warte, bis es auf GetIt veröffentlicht wird. Hier geht es zum entsprechenden Thread: ![]() Das nur so nebenbei. Dann stellte ich mir die Frage: Gibt es generelle Möglichkeiten, die Laufzeit von Code zu verbessern/optimieren, ohne ein Informatikstudium absolviert zu haben? Gibt es Literatur oder Videos dazu, die auch ein Laie versteht? Ich habe zum Beispiel das hier gefunden: Ein Vortrag von Prof. Dr. Nikolaus Wulff mit dem Thema "Systementwicklung" ![]() Darin geht es überwiegend um die Potenzierungsfunktion und wie man diese optimieren kann. Ich bin sehr gespannt auf eure Antworten! Truther |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Mal ein Beispiel aus meinem Fraktalgenerator:
Zuerst das Original:
Delphi-Quellcode:
und dann die schnellere Methode:
FUNCTION Formel101(a : Extended;
b : Extended; Faktor : Extended; Iteration : Longint; Art : Integer ):LongInt; VAR x,y,z : Extended; Farbe : INTEGER; BEGIN (* Apfelmann mit Spitze nach links *) x := 0; y := 0; Farbe := 0; REPEAT z := x * x - y + y + 2 * a; // x² - y² + 2a y := 2 * x * y + 3 * b; // 2 * x * y + 3b x := z; Farbe := Farbe + 1; UNTIL (Farbe = Iteration) OR (x * x + y * y > Faktor); Formel101 := GetFarbe(Farbe,x * x,y * y,x * y,Iteration,art); END;
Delphi-Quellcode:
Wenn's um Rechnerei geht, kann man eigentlich recht pauschal sagen:
FUNCTION Formel101(a : Extended;
b : Extended; Faktor : Extended; Iteration : Longint; Art : Integer ):LongInt; VAR x,y,z : Extended; aa, bb : Extended; xx,yy : Extended; xy : Extended; Farbe : INTEGER; BEGIN (* Apfelmann mit Spitze nach links *) x := 0; y := 0; Farbe := 0; aa := a + a; bb := b + b + b; xx := 0; yy := 0; REPEAT z := xx - yy + aa; // x² - y² + 2a xy := x * y; y := xy + xy + bb; // 2 * x * y + 3b x := z; xx := x * x; yy := y * y; Farbe := Farbe + 1; UNTIL (Farbe = Iteration) OR (xx + yy > Faktor); Formel101 := GetFarbe(Farbe,xx,yy,xy,Iteration,art); END; Alle Berechnungen, deren Ergebnis mehr als einmal benötigt wird, wird nur einmal berechnet und das Ergebnis in einer Variabel gespeichert. Danach wird statt der Berechnung die Variabel genommen. Der Geschwindigkeitsvorteil kann dann schonmal (je nach Komplexität) um die 25% (wenn man Glück hat aber auch deutlich mehr) betragen. Bei Grafikbearbeitung ist es hilfreich, für die Zeit der Berechnung alle Bildschirmaktuallisierungen zu deaktivieren. Ggfls. das Programm zu Beginn der Berechnung minimieren und am Ende der Berechnung wieder auf die normale Größe bringen. Das langsamste bei der Grafikbearbeitung ist halt die Darstellung auf dem Bildschirm. |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Es gibt da generell zwei Ansätze:
Der erste wäre der, einen Algorithmus zu verwenden, der "prinzipiell schneller" ist. Der erste Schritt dazu ist, die sogenannte Komplexität des vorhandenen Algorithmus abzuschätzen. Das ist ein Teil, den man im Theorie-Teil des Informatik-Studiums lernt. Allerdings könnten die Grundlagen dazu auch schon in der Schule gelehrt werden, imho. Zur Verdeutlichung ein ganz einfaches Beispiel: Du hast eine sortierte Liste mit Zahlen gegeben, und möchtest wissen, an welcher Stelle dieser Liste eine Zahl X steht. Möglichkeit 1: du schaust dir jedes einzelne Element der Liste an und vergleichst es mit X. Dabei nutzt du die Sortierung der Liste aber nicht aus. Möglichkeit 2: Du nutzt die Sortierung der Liste: Dazu schaust du dir zuerst das Element in der Mitte der Liste an. Ist das gesuchte X größer, brauchst du die ganze erste Hälfte der Liste nicht mehr zu checken - dort kann es nicht sein. Im anderen Fall kannst du die zweite Hälfte weglassen. Und dann suchst du mit dem gleichen Verfahren nur in dem interessanten Teil weiter. Der wesentliche Unterschied zwischen den beiden Verfahren ist, dass die Binärsuche konzeptionell schneller ist: Die suche ist nach grob Log(n)-vielen Schritten abgeschlossen (n ist die Anzahl der Elemente in der Liste), bei dem ersten Verfahren benötigt man grob n viele Schritte. Mit etwas mehr Mathematik liest sich das dann so: ![]() Der zweite Ansatz ist, den Algorithmus generell so zu belassen, aber die Ausführungsgeschwindigkeit dadurch zu verbessern, dass man das Verfahren geschickter implementiert. Bei der Bildverarbeitung ist z.B. ein Klassiker für sehr, sehr langsamen Code die Verwendung von
Delphi-Quellcode:
, um die RGB-Werte eines Pixels im Bild zu bestimmen. Wenn man so einen Code auf
Bitmap.Canvas.Pixels[]
Delphi-Quellcode:
umschreibt, wird der Code um ein vielfaches schneller, auch wenn der zugrundeliegende Algorithmus die gleiche asymptotische Zeitkomplexität hat.
Scanline
|
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
|
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Delphi-Quellcode:
Project861.dpr.8: Farbe := 0;
0040A104 33C0 xor eax,eax 0040A106 A388F54000 mov [$0040f588],eax Project861.dpr.9: Farbe := Farbe + 1; 0040A10B FF0588F54000 inc dword ptr [$0040f588] Project861.dpr.10: Inc(Farbe); 0040A111 FF0588F54000 inc dword ptr [$0040f588] Project861.dpr.11: end. 0040A117 E8B0BBFFFF call @Halt0 |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Zitat:
|
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
@Redeemer
Schön, dass Du es besser weißt. Aber diese Methode ist durch ausprobieren entstanden und dabei hat sich gezeigt, das bei vielen tausenden Berechnungen a + a schneller als 2 * a ist und das Farbe := Farbe + 1 schneller als Inc(Farbe) ist, auch wenn immer wieder behauptet wird, Inc sei schneller. (Wobei man hier vermutlich deutlich zwischen dem Inc von Pascal und dem Inc im erzeugten Maschinencode unterscheiden muss, bei 'nem intelligenten Compiler dürfte / sollte da der gleiche Maschinencode rauskommen. - siehe Uwes Beispiel ;-)) Und es ist nicht auszuschließen, dass sich identischer Quelltext unter einem anderen Compiler (in Bezug auf die Ausführungsgeschwindigkeit) vollkommen anders verhält, einfach weil anderer Maschinencode daraus kommt. Die Routine wurde halt nur von Turbo-Pascal 5 bis Delphi 7 in der Form genutzt. Vielleicht gehen neuere Delphicompiler oder Lazarus und FreePascal damit vollkommen anders um. Sprich: Es kommt nicht darauf an, ob
Delphi-Quellcode:
schneller als
Farbe := Farbe + 1
Delphi-Quellcode:
ist, sondern darauf, wie der Compiler das letztlich umsetzt. Und dann nimmt man jeweils die schneller Variante ;-) Die wesentliche Zeitersparnis liegt eh in der Reduzierung der Multiplikationen, die fressen die Zeit ;-)
Inc(Farbe)
Außerdem war nach Ideen gefragt und nicht nach ultimativ immer richtigen Vorgehensweisen ;-) Derweil: Die gibt es (vermutlich) nicht. Und ja: Die Berechnung in der Schleife kann man in der Tat noch ein bisserl optimieren. |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Delphi-Quellcode:
den gleichen Code wie für
Farbe := Farbe + 1
Delphi-Quellcode:
auszugeben. Andere Plattformen kommen wegen Extended aber auch wohl nicht in Frage.
Inc(Farbe)
|
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
|
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Diese Diskussion, wie man den Code so schreibt, dass man noch ein paar Takte sparen kann, ist sicherlich interessant. Aber ich denke, dass die hier nicht zielführend ist.
Wenn ich Truther richtig verstehe, dann hat er keine High-Performance-Situation, wo es wichtig ist, dass man noch die letzten paar Mikro- oder gar Nanosekunden einspart. Ich denke eher, dass sein Programm "spürbar" zu lange dauert. Ob das in dem Fall nun ein paar Sekunden sind, oder evtl. sogar in den Minutenbereich geht, kann ich nicht abschätzen. Da liegt der Flaschenhals mit ziemlicher Sicherheit nicht bei "inc vs. x+1", sondern ganz woanders. Bei "Bilder auf Ähnlichkeit prüfen" würde ich wie gesagt ganz stark auf "Canvas.Pixels" tippen, oder generell auf ein Verfahren mit kubischer Laufzeit (also 3-fach ineinander geschachtelte Schleife o.ä.), für das es ggf. sinnvollere Alternativen gibt. Wirklich konkret kann man aber ohne weitere Details zur Aufgabe und dem vorhandenen Code nicht werden. Denn wie in den verlinkten Folien in der Zusammenfassung schön zitiert: "Algorithmen zu optimieren ist eine Kunst". |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Mich wundert, dass noch kein Profiler angesprochen wurde. Es gibt Tools, die analysieren welcher Teil des Codes wie lange benötigt. Daraus kann man dann ableiten wo man optimieren sollte, wenn möglich.
Die wirklich guten Profiler sind nicht gerade billig, aber dieser hier ist für den Anfang ganz gut: ![]() Quelltext: ![]() |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Delphi-Quellcode:
und
ListBox.Items.BeginUpdate
Delphi-Quellcode:
einwerfen, was sich auf das Neuzeichnen von grafischen Komponenten bezieht.
ListBox.Items.EndUpdate
Zitat:
... Okay, offenbar gibt es gute Videos auf z.B. YouTube, die das gut erklären (siehe unten). Zitat:
Zitat:
Delphi-Quellcode:
, womit ich nur auf einen Farbkanal (Rot) eines Pixel zugreife, wenn ich mich nicht irre. Als ich das durch
Image.Colors[X, Y].Red
Delphi-Quellcode:
ersetzt hatte, war es deutlich schneller, auch wenn ich nicht weiß, warum es an sich schneller ist.
ScanLine
Auszug aus einem Wikipedia-Artikel zum Thema "Overhead": Zitat:
Zitat:
![]() Solche Sachen wären auch interessant zu wissen. Aber da es auch mitunter von der Programmiersprache abhängt (Aussage aus dem Video), kann man das nicht so sagen, das Addieren schneller als Multiplizieren ist. Das wäre dann in etwa gleichzusetzen mit der Problematik oben zu
Delphi-Quellcode:
und
Inc()
Delphi-Quellcode:
.
i := i + 1
Wenn ich asymptotische Laufzeit richtig verstanden habe, bedeutet das, dass man grob sagen kann, wie die Laufzeit wäre (z.B linear oder quadratisch), aber nicht exakt bestimmen kann, wie der exakte Verlauf der Kurve ist, richtig? Zitat:
Zitat:
Zitat:
Bei der Speicherreservierung (Allokation) bspw. für dynamische Arrays habe ich gelesen, dass es sinnvoll ist, wenn, sobald man mehr Elemente eines solchen Arrays hinzufügen möchte, die Länge des Arrays jeweils verdoppelt. Artikel dazu: ![]() Zitat:
Auch hatte ich mal aufgeschnappt, dass das Arbeiten mit Pointern die Laufzeiten auch verbessern können. Ob das stimmt, weiß ich nicht. Ich wüsste auch nicht, wie da der Zusammenhang wäre. @jaenicke Das werde ich mir mal ansehen, auch wenn vielleicht nicht für Delphi sondern für Lazarus. Für Lazarus gibt es wohl auch solch Profiler. ( ![]() |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Scanline zu verwenden ist schonmal ein guter Ansatz, allerdings ist auch jeder Scanline-Aufruf ziemlich langsam (zumindest in Delphi, keine Ahnung, ob das bei Lazarus auch so ist). Da lohnt es sich, die einzelnen Zeilen selbst per Pointer-Artithmetik zu ermitteln.
|
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Bei so kleinen Schleifen, die sehr oft schnell durchlaufen werden, funktionieren die Profiler auch nicht unbedingt sehr gut,
aber ansonsten sind die schon ganz praktisch. Bezüglich Scanline: Man kann das auch einmal am Anfang in ein Array auslesen oder nur den Anfang (eigentlich das Ende) sich holen, in einen Pointer, und dann Zeilen/Spalten entsprechend daraus berechen. |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
|
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Dank des Internets sind da draußen umglaubliche massen an Beispielen wie man dies oder jenes Problem angehen kann, und selber experimentieren ist schon immer der beste Weg gewesen etwas neues zu erfinden. |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Natürlich muss es
Delphi-Quellcode:
heißen:pale:
z := x * x - y * y + 2 * a; // x² - y² + 2a
|
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Delphi-Quellcode:
besser
2*a
Delphi-Quellcode:
schreibt...:-D
a + a
Gruß, Adreas |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Beim Integer ist Mal schneller, weil ja "nur" ein Shift.
Aber hätte gedacht, dass es beim Float auch zutrifft. |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Ich bin hier auf diese Seite gestoßen, die sich mit Optimierung von Code befasst.
Link: ![]() Was sind eure Meinungen dazu? Vielleicht könnte man auf jeden einzelnen Punkt davon eingehen und näher erläutern, sofern das nicht auf der Seite selbst geschehen ist. Möglicherweise könnte man diese Liste eben auch erweitern. Nachtrag: In einem Artikel zu Fast direct pixel gibt es einen Punkt zu Pointer. Dort steht: Zitat:
![]() Warum ist das Nutzen von Pointer in dem Fall schneller? Das würde sich auf die Implementierungsfrage beziehen, wie oben an dem Beispiel von
Delphi-Quellcode:
.
ScanLine
Könnte man sagen, dass das Nutzen von Pointern in der Regel schneller ist? |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Aber schneller als andere Methoden? Nicht wirklich. Wenn es dir auf Speed ankommt dann vezichte darauf das Bild in irgend einem Objekt (oder einer Komponente) zu lassen. Ein Bild kann auch als eindimensionales Array gesehen werden. Im Regelfall aus drei Bytes, eins pro Farbe. Noch schneller wird der Zugriff mit 4 Bytes. Da die Speicherzugriffe dann noch weiter optimiert werden können. Das 4. Byte wird dann einfach nicht werwendet, Speicherverschwendung aber egal wenn es nur um Speed geht. Also nach dem laden des Bildes die Pixel in ein Array kopieren und mit dem Array arbeiten. Den Index zum Zugriff berechnet man sich einfach selber um weiterhin Zeilen und Spalten zu haben. |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
(Pointer auf ArrayDaten) + (Datensatzgröße) * (1. Dimension) * y + (Datensatzgröße) * x Je nachdem wie gut der Compiler das optimiert wird das Ergebnis gespeichert und kann für mehrere aufeinanderfolgende Zugriffe auf einen Datensetz verwendt werden. Wenn sehr gut optimiert wird, wird in einer Schleife der Pointer immer nur um einen Offset erhöht. Im schlimmsten (unoptimierten) Fall wird diese Berechnung bei jedem Zugriff auf ein Feld des Datensatzes neu durchgeführt. Da aber der Delphi-Compiler dabei nicht besonders gut optimiert, kann man den Zugriff deutlich beschleunigen, indem man diese Pointer-Arithmetik selbst implementiert. Beim Zugriff auf die ScanLine Property kommt noch dazu, dass die Getter-Funktion noch komplexere Berechnungen anstellt als einfach nur Pointer-Arithmetik. (Alles bezogen auf Delphi, ich habe keine Ahnung, wie gut Lazarus / Free Pascal solche Zugriffe optimiert.) |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
>> Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Nachdem ich gelesen habe was du geschrieben hast komme ich nur zu dem Schluß "selbst-schreiben". Nur so kannst du das volle potenzial ausschöpfen. "vorgefertigter" code kann gut sein, doch die sind meist auf stabilität aufgebaut und nicht auf geschwindigkeit. Jetzt musst du halt für dich selber entscheiden was dir wichtiger ist, das erlangen neuen wissens um daraus eine hochoptimierrte variante zu entwickeln die wiederum in der herstellung viel zeit kostet, oder nehme produkt xyz mit dessen bereitgestellten code und lebe mit der geschwindigkeit wie sie ist. |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
|
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Generic vs Specific |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Mal etwas klugscheissend: Man kann auch darauf warten, daß die Rechner schneller werden.
Das Projekt, welches ich betreue, ist so 20 Jahre oder Teile davon sogar älter. Damals mußten einige Dinge sehr zeitoptimiert ablaufen, weil die damaligen Rechner es sonst nicht schafften, die Daten zu wuppen und darzustellen. Der Code basiert z.B. auf Reservierung von Speicher und dann die Zugriffe über Pointer. Jede Änderung am Code bedeutet immer wieder eine intensive Einarbeitung und ist extrem fehleranfällig. Heutzutage würde man diese Code-Konstrukte durch dynamische Arrays implementieren, was viel einfacher zu verstehen und wartbarer wäre. Was ich damit sagen will: Man sollte sich bei solchen "tricky" Optimierungen nur auf die notwendigsten Bereiche beschränken. Uns wurde schon vor 25-30 Jahren im Studium von einem Prof. gepredigt, daß Code möglichst immer leicht verständlich sein sollte. Er warnte damals vor Code, der zwar z.B. in genial wenigen Zeilen umgesetzt wurde, aber eben "tricky" und schwer verständlich ist. Ich meine, man kann rechenintensive Sachen auch in die Grafikkarte auslagern. Ich meine, das geht mit so ziemlich allem Krams. Ich hab da so im Hinterkopp OpenCL. |
AW: Möglichkeiten Code zu optimieren (z.B. Laufzeit verringern)
Zitat:
Ich hab noch nie mit so Profilern gearbeitet. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:36 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