Inwieweit ist die generische und Metaprogrammierung mit C ++ - Vorlagen in der Computerwissenschaft nützlich?

17

Die C ++ - Sprache bietet allgemeine Programmierung und Metaprogrammierung über Vorlagen. Diese Techniken haben ihren Weg in viele große wissenschaftliche Computerpakete (z. B. MPQC , LAMMPS , CGAL , Trilinos ) gefunden. Aber was haben sie tatsächlich dazu beigetragen, dass das wissenschaftliche Rechnen einen Wert hat, der über nicht generische Nicht-Metasprachen wie C oder Fortran hinausgeht, und zwar in Bezug auf die Gesamtentwicklungszeit und die Verwendbarkeit für gleiche oder angemessene Effizienz?

Hat die generische und Metaprogrammierung mithilfe von C ++ - Vorlagen bei einer wissenschaftlichen Rechenaufgabe eine Verbesserung der Produktivität, Expressivität oder Benutzerfreundlichkeit gezeigt, gemessen an gut bekannten Benchmarks (Codezeilen, Personalaufwand usw.)? Welche Risiken sind dementsprechend mit der Verwendung von C ++ - Vorlagen für die generische und Metaprogrammierung verbunden?

Todeshauch
quelle
Ich bin besorgt, dass diese Frage zu offen für Meinungen sein könnte, aber ich möchte sehen, was die Leute in der Gemeinde sagen.
Geoff Oxberry
1
Ich habe den Kommentar entgleist vollständig gelöscht. Wenn Sie möchten, dass es zu einem Chat oder zu einem Meta weitergeleitet wird, mache ich das gerne. Siehe meine Meta hier .
Aron Ahmadia
3
Lesen Sie auch die entsprechende Frage hier, um zu erfahren, wann Sie Ausdrucksvorlagen verwenden und wann Sie sie vermeiden sollten .
Aron Ahmadia
Ich denke nicht, dass es richtig ist zu sagen, dass LAMMPS Vorlagen oder Metaprogramme verwendet. LAMMPS ist objektorientierter Code, der Fortran die meiste Zeit sehr ähnlich sieht. Ich denke, MPQC hat auch nicht viel Templating, aber es ist stark objektorientiert und polymorph.
Jeff
1
OpenFOAM nutzt Vorlagen und andere Funktionen von C ++.
Dohn Joe

Antworten:

14

Ich denke, im Großen und Ganzen hat sich die Template-Metaprogrammierung in der Praxis als unbrauchbar erwiesen - sie kompiliert zu langsam und die Fehlermeldungen, die wir erhalten, sind einfach nicht zu entziffern. Auch bei der Metaprogrammierung ist die Eintrittsbarriere für Einsteiger einfach zu hoch.

Natürlich ist die generische Programmierung ein völlig anderes Thema, wie Trilinos, deal.II (meine eigene Bibliothek), DUNE und viele andere Bibliotheken bezeugen. und die Community hat es weitgehend akzeptiert, solange es sich in Grenzen hält, die die Probleme der Metaprogrammierung vermeiden. Ich denke, generisches Programmieren ist ein offensichtlicher Erfolg.

Natürlich ist keines dieser Themen unmittelbar mit OOP verbunden. Ich würde sagen, OOP wird von der wissenschaftlichen Computergemeinschaft allgemein akzeptiert. Auch wenn es sich nicht um generisches Programmieren handelt, ist es kein Thema der Debatte: Jede erfolgreiche Bibliothek, die in den letzten 15 Jahren geschrieben wurde (ob in C ++, C oder Fortran), verwendet OOP-Techniken.

Wolfgang Bangerth
quelle
4
tmp mag für unerfahrene Benutzer schwierig sein, aber es wird oft sehr gut in der Bibliothek gemacht. Es ist eine dieser Techniken, die die Menge an Code wirklich reduzieren können, aber Sie müssen wirklich wissen, was Sie tun. Wenn du mir nicht glaubst, lies die Quelle von Eigen oder Elemental. Sein schöner Code, der ohne Vorlagen so ziemlich unmöglich wäre.
Aterrel
5
Sicher, es ist eine Technik mit Wert. Es ist jedoch schwierig zu warten und oft schwierig zu verwenden, wenn es der externen Schnittstelle ausgesetzt ist. Ich denke jedoch, einer der Gründe, warum TMP nicht ganz der Erfolg war, den die Leute ursprünglich erwartet hatten, ist, dass die Compiler gerade sehr gut geworden sind. TMP lebt von der Tatsache, dass viele Dinge zur Kompilierungszeit bekannt sind und dann als Konstanten in den eigentlichen Code übertragen werden können. Aber Compiler sind in Inlining, Function Cloning usw. ziemlich gut geworden, und so kann ein erheblicher Teil des Nutzens heutzutage mit "normaler" Programmierung erzielt werden.
Wolfgang Bangerth
15

Lassen Sie mich ein Beispiel geben, das auf Erfahrung basiert. Die meisten Bibliotheken, die ich von Tag zu Tag benutze, verwenden in irgendeiner Weise OOP. OOP ist in der Lage, die Komplexität zu verbergen, die für viele Domänen erforderlich ist. Es ist kein Mechanismus, der wirklich zur Leistung beiträgt. Was passieren kann, ist, dass eine Bibliothek bestimmte Optimierungen basierend auf der Objekthierarchie verwenden kann, aber zum größten Teil geht es darum, die Komplexität vor dem Benutzer zu verbergen. Nachschlagen von Entwurfsmustern. Dies sind die Mechanismen, die häufig eingesetzt werden, um diese Komplexität zu verbergen.

Nehmen Sie als Beispiel PETSc. PETSc verwendet ein Inspector / Executor-Modell von OOP, bei dem einer seiner Algorithmen die verfügbaren Routinen in einem bestimmten Objekt untersucht und auswählt, welche zur Ausführung der Routine ausgeführt werden sollen. Dies ermöglicht es einem Benutzer, Bedenken zu trennen, zum Beispiel kann die Matrixaktion jede Art von blockierter oder optimierter Routine enthalten und von zahlreichen iterativen Lösern effektiv verwendet werden. Indem der Benutzer die Möglichkeit erhält, seine eigenen Datentypen und Auswertungen anzugeben, werden einige wichtige Routinen beschleunigt und die gesamte Funktionalität der Bibliothek bleibt weiterhin verfügbar.

Ein weiteres Beispiel ist FEniCS und deal.II. Beide Bibliotheken verwenden OOP, um eine große Anzahl von Finite-Elemente-Methoden zu verallgemeinern. In beiden ist alles von Elementtyp, Elementreihenfolge, Quadraturdarstellung usw. austauschbar. Obwohl beide Bibliotheken "langsamer" sind als einige speziell strukturierte FEM-Codes, können sie eine Vielzahl von Problemen lösen, wobei ein Großteil der Komplexität von FEM dem Benutzer unbekannt ist.

Mein letztes Beispiel ist Elemental. Elemental ist eine neue Bibliothek für dichte lineare Algebra, die die Verwaltung von MPI-Kommunikatoren und der Datenposition zu einem sehr einfachen Sprachkonstrukt gemacht hat. Das Ergebnis ist, dass Sie, wenn Sie einen FLAME-Seriencode haben, durch Ändern der Datentypen auch einen Parallelcode über Elemental erhalten können. Noch interessanter ist, dass Sie mit der Datenverteilung spielen können, indem Sie die Verteilung gleich einer anderen einstellen.

OOP sollte als ein Weg betrachtet werden, die Komplexität zu managen, und nicht als Paradigma für den Wettbewerb mit handgewalzten Baugruppen. Auch wenn Sie es schlecht machen, entsteht viel Overhead. Daher müssen Sie das Timing beibehalten und die Mechanismen aktualisieren, mit denen Sie es verwenden.

aterrel
quelle
3

Was Sprachfunktionen OOPfür das wissenschaftliche Rechnen tun, sind kompaktere Code-Anweisungen, die das Verständnis und die Verwendung von Code verbessern. Beispielsweise müssen FFTRoutinen eine große Anzahl von Argumenten für jeden Funktionsaufruf enthalten, was den Code umständlich macht.

Durch die Verwendung von moduleoder class-Anweisungen kann nur das übergeben werden, was zum Zeitpunkt des Aufrufs benötigt wird, da sich die restlichen Argumente auf die Problemeinrichtung beziehen (dh Größe der Arrays und Koeffizienten).

Nach meiner Erfahrung hatte ich SUBROUTINEAufrufe mit 55 Argumenten (rein und raus) und reduzierte diese auf 5, um den Code besser zu machen.

Das ist Wert.

ja72
quelle
3

Ich bin ein starker Befürworter der generischen Programmierung und der Metaprogrammierung für das wissenschaftliche Rechnen. Ich entwickle gerade eine freie Software-C ++ - Bibliothek für Galerkin-Methoden, die auf diesen Techniken basiert und Feel ++ (http://www.feelpp.org) heißt und stetig an Fahrt gewinnt. Es stimmt, dass es immer noch Schwierigkeiten gibt, wie langsame Kompilierungszeiten und dass die Lernkurve steil sein kann, wenn man verstehen will, was sich hinter den Kulissen abspielt. Dies ist jedoch äußerst interessant und umwerfend. Wenn Sie auf Bibliotheksebene arbeiten und die Komplexität hinter einer domänenspezifischen Sprache verbergen, erhalten Sie ein äußerst leistungsfähiges Tool. Wir verfügen über ein sehr breites Spektrum an Methoden, die wir anwenden und vergleichen können. Für Lehrzwecke des wissenschaftlichen Rechnens ist dies fantastisch, für die Forschung und auch für neue numerische Methoden, für Anwendungen in großem Maßstab. na wir arbeiten dran aber soweit so gut, können wir schon ein paar nette sachen machen. Wir haben Ingenieure, Physiker und Mathematiker, die es benutzen: Die meisten benutzen nur die Sprache für die Variationsformulierung und sie sind damit zufrieden. Wenn ich einige Formulierungen betrachte, die unsere Physikerkollegen manipulieren, würde ich nicht wollen, dass sie "von Hand" ohne eine Hochsprache zur Beschreibung der Variationsformulierung gemacht werden. Ich persönlich bin der Meinung, dass diese "Techniken" oder "Paradigmen" jetzt notwendig sind, um die Komplexität von wissenschaftlichem Computercode zu bewältigen, indem die Codegröße mit einem riesigen Faktor multipliziert werden muss. Möglicherweise muss die Unterstützung der Metaprogrammierung in C ++ verbessert werden, aber sie ist bereits in gutem Zustand, insbesondere seit C ++ 11.

christophe Prud'homme
quelle
2

Möglicherweise finden Sie das Dokument http://arxiv.org/abs/1104.1729, das für Ihre Frage relevant ist. Es werden Ausdrucksvorlagen (eine bestimmte Anwendung der Metaprogrammierung von Vorlagen, die im wissenschaftlichen Code verwendet wird) aus der Perspektive der Leistung erörtert.

Juan M. Bello-Rivas
quelle
Das Papier macht mich verrückt. Vergleichen Sie den schnellsten normalen Fortran mit MKL und er verliert ebenfalls. Man strebt nicht nach handgestimmter Montage, sondern danach, wenn jede Nanosekunde zählt und von einer sehr großen Anzahl von Leuten wiederverwendet werden kann.
Aterrel
@aterrel: Das ist genau der Kontrast, über den ich mich wundere. Da Sie wissen, dass Sie als allerletzten Entwicklungsschritt eine Handoptimierung durchführen müssen, welche Sprache würden Sie vor dem letzten Entwicklungsschritt als Basis wählen? Haben wir schwierige Daten, um die gewünschte Sprache vorzuschlagen?
Deathbreath
9
@Deathbreath: Ich werde eine Antwort wiederholen, die ich auch bei mehreren anderen Threads gegeben habe. Im Großen und Ganzen tun Sie das letzte bisschen Geschwindigkeit aus Ihrem Code heraus, was Sie sehr selten tun. Aber Sie programmieren die ganze Zeit Algorithmen auf hoher Ebene. Wählen Sie also die Sprache, mit der Sie die großen Aufgaben schnell erledigen können. Es gibt immer eine Möglichkeit, Low-Level-Inhalte irgendwie einzubeziehen, aber es sollte nicht das sein, was Ihre Wahl der Programmiersprache bestimmt.
Wolfgang Bangerth
0

Vorlagen sind sehr gut darin, Typ- / Domänenprüfungen zur Laufzeit zu entfernen. Diese können zum Zeitpunkt der Kompilierung erledigt werden. Dies kann theoretisch die Leistung gegenüber derselben Art der Implementierung in C oder Fortran steigern, bei der die Typprüfung nur zur Laufzeit durchgeführt werden kann. Überprüfungen werden im Quellcode implementiert. Sie können jedoch mit Precompiler-Optionen dieselben Ergebnisse in C erzielen, diese müssen jedoch im Gegensatz zu Vorlagen von Hand ausgeführt werden.

Vorlagen können jedoch auch einen erheblichen Overhead verursachen. Sie können häufig Code Bloat erzeugen, was sich auf die Verwendung des Anweisungs-Cache auswirken kann. Darüber hinaus können generische Ansätze den Compiler bei der Optimierung oft in Schwierigkeiten bringen - bei der Verwendung generischer Ansätze ist die Codeanalyse nicht immer einfach. Es ist immer ein Problem mit der Automatisierung - einschließlich der Compileroptimierung -, sehr oft ist der Ausgabecode nicht cachefreundlich.

Die Vorteile der Typ- / Domänenprüfung sind zwar sicherer, aber der einzige wirkliche Vorteil, den ich in Bezug auf die Leistung sehe, und diese sind normalerweise nicht wahrnehmbar. Aber wie gesagt, der Gesamteffekt kann negativ sein, je nachdem, was Sie tun. Aus diesem Grund ist es oft besser, Ihren Code von Hand zu optimieren, wenn Sie erhebliche Engpässe haben.

cdcdcd
quelle