In manchen Fällen weiß man zur Kompilierzeit, wie ein bestimmtes Stück algorithmische Daten aussieht und möchte diese Informationen dem Compiler übermitteln. In dieser Frage geht es darum, wie man das am besten erreichen kann.
Betrachten Sie als Beispiel das folgende Beispiel einer Sparse-Matrix-Multiplikation, bei der die Matrix zur Kompilierzeit konstant und bekannt ist:
%Vor%In einem solchen Fall könnte eine vollständig verzweigungsfreie Implementierung geschrieben werden, um die Matrixvektormultiplikation für einen beliebigen Eingabevektor zu implementieren:
%Vor%, das das korrekte Ergebnis für die Matrix-Vektor-Multiplikation ( 81799, 9321, 0, 29089, 6435
) ausgibt.
Es können weitere Optimierungen in Betracht gezogen werden, die auf datenspezifischem Wissen über den Speicherort der Referenz aufbauen.
Jetzt ist klar, dass dies ein Ansatz ist, der verwendet werden kann, aber er wird unhandlich, wenn die Größe der Daten groß wird (sagen wir ~ 100 MB in meinem Fall) und auch in jeder realen Welt von Meta-Programmierung abhängig wäre erzeugt das zugehörige datenabhängige Wissen.
Hat die generelle Strategie des Backens von datenspezifischem Wissen hinsichtlich der Optimierung einen großen Erfolg? Wenn ja, was ist der beste Ansatz dafür?
Im obigen Beispiel wird das Ganze auf einer Ebene auf das Wissen um ARRAY_SIZE
mit den zur Laufzeit gesetzten Arrays reduziert. Dies führt mich zu der Annahme, dass der Ansatz begrenzt ist (und es ist wirklich ein Datenstrukturproblem), aber ich bin sehr interessiert zu wissen, ob der allgemeine Ansatz der Daten abgeleiteten Kompilierzeit-Optimierungen in jeder Situation nützlich ist.
Ich denke nicht, dass dies eine sehr gute Antwort auf diese Frage ist, aber ich werde versuchen, es trotzdem anzubieten. Es ist auch mehr eine Suche nach der gleichen grundlegenden Antwort.
Ich arbeite in 3D VFX einschließlich Raytracing, wo es nicht unüblich ist, eine relativ bescheidene Eingabe mit Datenstrukturen zu machen, die in weniger als einer Sekunde aufgebaut sind, und dann eine monumentale Menge an Verarbeitung zu dem Punkt zu machen, an dem ein Benutzer Stunden warten könnte Qualitätsproduktion rendert in einer schwierigen Lichtsituation.
Zumindest theoretisch könnte das viel schneller gehen, wenn wir diese "datenspezifischen Optimierungen" machen könnten. Variablen könnten in Literalkonstanten umgewandelt werden, wesentlich weniger Verzweigungen könnten erforderlich sein, Daten, von denen bekannt ist, dass sie immer eine obere Grenze von 45 Elementen haben, könnten auf dem Stapel statt auf dem Heap zugewiesen werden oder eine andere Form von Speicher verwenden, der im Voraus vorbelegt wurde Die Vektorisierung könnte besser genutzt werden als je zuvor, die Vektorisierung könnte leichter durchgeführt werden, und die Erreichung von Sicherheit und Effizienz der Threads könnte viel einfacher sein, usw.
Wo dies für mich unangenehm ist, ist, dass dies Informationen über Benutzereingaben erfordert, die nur nach dem üblichen Begriff "Kompilierzeit" bereitgestellt werden können. Ein Großteil meines Interesses bezieht sich hier auf Techniken zur Codegenerierung, während die Anwendung läuft.
Nun, klar ist dies ein Ansatz, der verwendet werden kann, aber es beginnt Unhandlich werden, wenn die Größe der Daten groß wird (sagen wir ~ 100MB in meinem Fall) und auch in jeder realen Weltsituation davon abhängen würde Meta-Programmierung, um das zugehörige datenabhängige Wissen zu generieren.
Ich denke darüber hinaus, wenn die Datengröße zu groß wird, brauchen wir oft einen guten Anteil an Verzweigungen und Variablen, nur um nicht so viel Code zu generieren, dass wir durch icache-Fehlschläge einen Engpass bekommen.
Aber selbst die Fähigkeit, ein Dutzend Variablen, auf die häufig zugegriffen wird, in Konstanten zur Kompilierzeit umzuwandeln und einer Handvoll Datenstrukturen zu erlauben, eine größere Kenntnis der spezifizierten Eingabe zu nutzen (und mit Hilfe eines aggressiven Optimierers), kann hier eine große Laufleistung ergeben. vor allem wenn man bedenkt, wie gut Optimierer sind, vorausgesetzt, sie haben die notwendigen Informationen im Voraus zur Verfügung gestellt.
Einige davon könnten normalerweise mit zunehmend ausgefeilteren und verallgemeinerten Codes, Metaprogrammierungstechniken usw. in Angriff genommen werden, doch es gibt einen Gipfel, wie weit wir dorthin gehen können: Ein Optimierer kann nur soviel optimieren, wie die Information im voraus verfügbar ist. Die Schwierigkeit besteht darin, diese Informationen auf praktische Weise bereitzustellen. Und wie Sie bereits vermutet haben, kann dies schnell unhandlich und schwierig zu verwalten sein, und die Produktivität wird genauso groß (wenn nicht größer) wie die Effizienz.
Die am meisten versprechenden Techniken drehen sich also um Codegenerierungstechniken, die auf eine bestimmte Problemdomäne abgestimmt sind, aber nicht für eine bestimmte Eingabe (die Optimierung für die spezifischen Eingaben wird sich mehr auf den Optimierer stützen, die Codegenerierung ist da, damit wir kann mehr von diesen Informationen zur Verfügung stellen, die für den Optimierer leichter / geeigneter sind). Ein bescheidenes Beispiel, das bereits so etwas tut, ist Open Shading Language, wo es eine JIT-Kompilation verwendet, die diese Idee auf eine bescheidene Ebene ausnutzt:
OSL verwendet das LLVM-Compiler-Framework zum Übersetzen von Shader-Netzwerken in Maschinencode im laufenden Betrieb (just in time oder "JIT") und dabei stark optimiert Shadern und Netzwerke mit vollen Kenntnissen der Shader-Parameter und andere Laufzeitwerte, die nicht hätten sein können bekannt, wenn die Shader aus dem Quellcode kompiliert wurden. Als Ergebnis haben wir sehen unsere OSL - Schattierungsnetzwerke um 25% schneller als die äquivalente Shader, handgefertigt in C! (So sind unsere alten Shader arbeitete in unserem Renderer.)
Obwohl eine 25% ige Verbesserung gegenüber handgeschriebenem Code bescheiden ist, ist das bei einem Produktions-Renderer immer noch eine große Sache, und es scheint, als könnten wir weit darüber hinausgehen.
Die Verwendung von Knoten als visuelle Programmiersprache bietet auch eine restriktivere Umgebung, die dazu beiträgt, menschliche Fehler zu reduzieren, Lösungen auf höherer Ebene auszudrücken, die Ergebnisse von Änderungen im laufenden Betrieb zu sehen (sofortige Bearbeitung) usw. - - Es erhöht nicht nur die Effizienz, sondern auch die Produktivität, die wir brauchen, um bei solchen Optimierungen nicht verloren zu gehen. Die Pflege und der Aufbau des Code-Generators könnte ein wenig komplex sein, aber es muss nur die minimale Menge an Code benötigt werden und die Komplexität wird nicht mit der Menge des erzeugten Codes skaliert.
Entschuldigung - das ist nicht gerade eine Antwort auf Ihre Frage als Kommentar, aber ich denke, wir suchen nach einer ähnlichen Sache.
Tags und Links optimization c