Oft schreibe ich sehr ähnlichen Code für ein-, zwei- und dreidimensionale Versionen einer bestimmten Operation / eines Algorithmus. Das Verwalten all dieser Versionen kann mühsam werden. Einfache Code-Generierung funktioniert ziemlich gut, aber es scheint, als gäbe es einen besseren Weg.
Gibt es eine relativ einfache Möglichkeit, eine Operation einmal zu schreiben und auf höhere oder niedrigere Dimensionen zu verallgemeinern?
Ein konkretes Beispiel ist: Angenommen, ich muss den Gradienten eines Geschwindigkeitsfeldes im Spektralraum berechnen. In drei Dimensionen würden die Fortran-Schleifen ungefähr so aussehen:
do k = 1, n
do j = 1, n
do i = 1, n
phi(i,j,k) = ddx(i)*u(i,j,k) + ddx(j)*v(i,j,k) + ddx(k)*w(i,j,k)
end do
end do
end do
wo das ddx
Array entsprechend definiert ist. (Man könnte dies auch mit Matrixmultiplikatoren tun.) Der Code für einen zweidimensionalen Fluss ist nahezu identisch, mit der Ausnahme, dass die dritte Dimension aus den Schleifen, Indizes und der Anzahl der Komponenten entfernt wird. Gibt es eine bessere Möglichkeit, dies auszudrücken?
Ein anderes Beispiel ist: Angenommen, ich habe Fluidgeschwindigkeiten, die punktweise auf einem dreidimensionalen Gitter definiert sind. Um die Geschwindigkeit auf einen beliebigen Ort zu interpolieren (dh nicht den Gitterpunkten zu entsprechen), kann der eindimensionale Neville- Algorithmus nacheinander über alle drei Dimensionen hinweg verwendet werden (dh Dimensionsreduktion). Gibt es eine einfache Möglichkeit zur Dimensionsreduktion bei eindimensionaler Implementierung eines einfachen Algorithmus?
quelle
Die Frage hebt hervor, dass die meisten "einfachen" Programmiersprachen (zumindest C, Fortran) es Ihnen nicht erlauben, dies sauber zu machen. Eine zusätzliche Einschränkung besteht darin, dass Sie eine einfache Schreibweise und eine gute Leistung wünschen .
Statt also das Schreiben eine Dimension spezifischen Code, sollten Sie schreiben einen Code, erzeugt eine Dimension spezifischen Code. Dieser Generator ist dimensionsunabhängig, auch wenn der Berechnungscode dies nicht ist. Mit anderen Worten, Sie fügen eine Argumentationsebene zwischen Ihrer Notation und dem Code ein, der die Berechnung ausdrückt. C ++ - Vorlagen haben dieselbe Bedeutung: Sie sind direkt in die Sprache integriert. Nachteil, sie sind etwas umständlich zu schreiben. Dies reduziert die Frage, wie der Codegenerator praktisch zu realisieren ist.
Mit OpenCL können Sie Code zur Laufzeit ziemlich sauber generieren. Es sorgt auch für eine sehr saubere Trennung zwischen 'äußerem Steuerungsprogramm' und 'inneren Schleifen / Kerneln'. Das äußere, generierende Programm ist weit weniger leistungsbeschränkt und kann daher genauso gut in einer bequemen Sprache wie Python geschrieben werden. Das ist meine Hoffnung, wie PyOpenCL verwendet wird - Entschuldigung für den erneuerten schamlosen Stecker.
quelle
Dies kann in jeder Sprache mit dem folgenden groben mentalen Prototyp erreicht werden:
Von da an geht es darum, gegen die Syntax Ihrer bestimmten Sprache zu kämpfen, um Ihren Code nd-konform zu halten.
Nachdem ich einen n-dimensionalen Fluiddynamik-Löser geschrieben habe , habe ich festgestellt, dass es hilfreich ist, eine Sprache zu haben, die das Entpacken eines listenähnlichen Objekts als Argument einer Funktion unterstützt. Dh a = (1,2,3) f (a *) -> f (1,2,3). Durch erweiterte Iteratoren (z. B. ndenumerate in numpy) wird der Code um eine Größenordnung übersichtlicher.
quelle
quelle
Die klaren Antworten, wenn Sie die Geschwindigkeit von Fortran beibehalten möchten, sind die Verwendung einer Sprache, die über die richtige Codegenerierung wie Julia oder C ++ verfügt. C ++ - Vorlagen wurden bereits erwähnt, daher erwähne ich hier Julias Tools. Mit den von Julia generierten Funktionen können Sie mithilfe der Metaprogrammierung Funktionen nach Bedarf über Typinformationen erstellen. Im Wesentlichen können Sie hier also Folgendes tun
Anschließend
N
erstellen Sie mit dem Befehl programmgesteuert den Code, den Sie ausführen möchten, da erN
dimensioniert ist. Dann können Julias kartesische Bibliothek oder Pakete wie Einsum.jl- Ausdrücke einfach für dieN
Dimensionsfunktion erstellt werden.Das Schöne an Julia ist, dass diese Funktion statisch kompiliert und für jedes neue dimensionale Array optimiert wird, sodass nicht mehr kompiliert wird, als Sie benötigen, und Sie trotzdem die C / Fortran-Geschwindigkeit erreichen. Am Ende ähnelt dies der Verwendung von C ++ - Vorlagen, es ist jedoch eine höhere Sprache mit vielen Werkzeugen, die es einfacher machen (leicht genug, dass dies ein nettes Hausaufgabenproblem für einen Studenten wäre).
Eine andere Sprache, die dafür gut ist, ist ein Lisp wie Common Lisp. Es ist einfach zu benutzen, da es Ihnen wie Julia den kompilierten AST mit einer Menge eingebauter Introspection-Tools bietet, aber im Gegensatz zu Julia wird es nicht automatisch kompiliert (in den meisten Distributionen).
quelle
Ich bin im selben (Fortran) Boot. Sobald ich meine 1D-, 2D-, 3D- und 4D-Elemente (ich mache projektive Geometrie) habe, erstelle ich für jeden Typ die gleichen Operatoren und schreibe dann meine Logik mit Gleichungen auf hoher Ebene, die klar machen, was los ist. Es ist nicht so langsam, wie Sie vielleicht glauben, separate Schleifen für jede Operation und viel Speicherkopie zu haben. Ich überlasse es dem Compiler / Prozessor, die Optimierungen vorzunehmen.
Beispielsweise
Zur Verwendung in Gleichungen wie
wo
e
undr
undg
kann jede Dimension haben, die mathematisch Sinn macht.quelle