Ich bin gespannt, wie die F # -Leistung mit der C ++ - Leistung verglichen wird. Ich habe eine ähnliche Frage in Bezug auf Java gestellt, und ich hatte den Eindruck, dass Java nicht für starkes Zahlenkalkulieren geeignet ist.
Ich habe gelesen, dass F # skalierbarer und leistungsfähiger sein soll, aber wie ist diese reale Leistung im Vergleich zu C ++? Spezifische Fragen zur aktuellen Implementierung sind:
- Wie gut macht es Gleitkomma?
- Erlaubt es Vektoranweisungen
- Wie freundlich ist es, Compiler zu optimieren?
- Wie groß ist der Speicherbedarf? Ermöglicht es eine fein abgestimmte Kontrolle über die Speicherlokalität?
- Hat es Kapazität für verteilte Speicherprozessoren, zum Beispiel Cray?
- Welche Funktionen können für die Computerwissenschaft von Interesse sein, wenn es um die Verarbeitung schwerer Zahlen geht?
- Gibt es tatsächliche wissenschaftliche Computerimplementierungen, die diese verwenden?
Vielen Dank
Antworten:
quelle
Variiert stark je nach Anwendung. Wenn Sie in einem Multithread-Programm in großem Umfang ausgefeilte Datenstrukturen verwenden, ist F # wahrscheinlich ein großer Gewinn. Wenn Sie die meiste Zeit in engen numerischen Schleifen verbringen, die Arrays mutieren, ist C ++ möglicherweise 2-3 × schneller.
Fallstudie: Ray Tracer Mein Benchmark verwendet hier einen Baum für hierarchisches Keulen und numerischen Ray-Sphere-Schnittcode, um ein Ausgabebild zu generieren. Dieser Benchmark ist mehrere Jahre alt und der C ++ - Code wurde im Laufe der Jahre Dutzende Male verbessert und von Hunderttausenden von Menschen gelesen. Don Syme von Microsoft hat es geschafft, eine F # -Implementierung zu schreiben, die etwas schneller als der schnellste C ++ - Code ist, wenn sie mit MSVC kompiliert und mit OpenMP parallelisiert wird.
Das Entwickeln von Code ist mit F # viel einfacher und schneller als mit C ++. Dies gilt sowohl für die Optimierung als auch für die Wartung. Wenn Sie mit der Optimierung eines Programms beginnen, führt der gleiche Aufwand zu viel größeren Leistungssteigerungen, wenn Sie F # anstelle von C ++ verwenden. F # ist jedoch eine übergeordnete Sprache und legt folglich eine niedrigere Obergrenze für die Leistung fest. Wenn Sie also unendlich viel Zeit für die Optimierung haben, sollten Sie theoretisch immer in der Lage sein, schnelleren Code in C ++ zu erstellen.
Dies ist genau der gleiche Vorteil, den C ++ gegenüber Fortran und Fortran gegenüber handgeschriebenen Assemblern hatte.
Fallstudie: QR-Zerlegung Dies ist eine grundlegende numerische Methode aus der linearen Algebra, die von Bibliotheken wie LAPACK bereitgestellt wird. Die Referenz-LAPACK-Implementierung umfasst 2.077 Zeilen Fortran. Ich habe eine F # -Implementierung in weniger als 80 Codezeilen geschrieben, die das gleiche Leistungsniveau erreicht. Die Referenzimplementierung ist jedoch nicht schnell: Vom Hersteller optimierte Implementierungen wie Intels Math Kernel Library (MKL) sind häufig zehnmal schneller. Bemerkenswerterweise konnte ich meinen F # -Code weit über die Leistung der Intel-Implementierung hinaus optimieren , die auf Intel-Hardware ausgeführt wird, während mein Code unter 150 Codezeilen und vollständig generisch gehalten wurde (er kann einfache und doppelte Genauigkeit sowie komplexe und sogar symbolische Matrizen verarbeiten!): Für große, dünne Matrizen ist mein F # -Code bis zu 3 × schneller als der Intel MKL.
Beachten Sie, dass die Moral dieser Fallstudie nicht darin besteht, dass Sie erwarten sollten, dass Ihre F # schneller ist als von Anbietern optimierte Bibliotheken, sondern dass selbst Experten wie Intel produktive Optimierungen auf hoher Ebene verpassen, wenn sie nur Sprachen auf niedrigerer Ebene verwenden. Ich vermute, Intels Experten für numerische Optimierung haben die Parallelität nicht vollständig ausgenutzt, weil ihre Tools sie extrem umständlich machen, während F # sie mühelos macht.
Die Leistung ist ähnlich wie bei ANSI C, einige Funktionen (z. B. Rundungsmodi) sind in .NET jedoch nicht verfügbar.
Nein.
Diese Frage macht keinen Sinn: F # ist eine proprietäre .NET-Sprache von Microsoft mit einem einzigen Compiler.
Eine leere Anwendung verwendet hier 1,3 MB.
Besser als die meisten speichersicheren Sprachen, aber nicht so gut wie C. Beispielsweise können Sie beliebige Datenstrukturen in F # entpacken, indem Sie sie als "Strukturen" darstellen.
Kommt darauf an, was du mit "Kapazität für" meinst. Wenn Sie .NET auf diesem Cray ausführen können, können Sie die Nachrichtenübergabe in F # verwenden (genau wie in der nächsten Sprache), aber F # ist hauptsächlich für Desktop-Multicore-x86-Computer vorgesehen.
Speichersicherheit bedeutet, dass Sie keine Segmentierungsfehler und Zugriffsverletzungen erhalten. Die Unterstützung für Parallelität in .NET 4 ist gut. Die Möglichkeit, Code im laufenden Betrieb über die interaktive F # -Sitzung in Visual Studio 2010 auszuführen, ist für interaktives technisches Computing äußerst nützlich.
Unsere kommerziellen Produkte für das wissenschaftliche Rechnen in F # haben bereits Hunderte von Benutzern.
Ihre Fragestellung zeigt jedoch, dass Sie wissenschaftliches Rechnen als Hochleistungsrechnen (z. B. Cray) und nicht als interaktives technisches Rechnen (z. B. MATLAB, Mathematica) betrachten. F # ist für Letzteres vorgesehen.
quelle
Zusätzlich zu dem, was andere sagten, gibt es einen wichtigen Punkt bei F # und das ist Parallelität . Die Leistung von normalem F # -Code wird von CLR bestimmt, obwohl Sie möglicherweise LAPACK von F # verwenden oder native Aufrufe mit C ++ / CLI als Teil Ihres Projekts tätigen können.
Gut gestaltete Funktionsprogramme lassen sich jedoch in der Regel viel einfacher parallelisieren. Dies bedeutet, dass Sie mit Multi-Core-CPUs, die Ihnen auf jeden Fall zur Verfügung stehen, wenn Sie wissenschaftliches Rechnen betreiben, auf einfache Weise an Leistung gewinnen können. Hier sind einige relevante Links:
In Bezug auf verteiltes Rechnen können Sie jedes verteilte Rechenframework verwenden, das für die .NET-Plattform verfügbar ist. Es gibt ein MPI.NET-Projekt, das gut mit F # funktioniert, aber Sie können möglicherweise auch DryadLINQ verwenden, ein MSR-Projekt.
quelle
Wie bei allen Sprach- / Leistungsvergleichen hängt Ihr Kilometerstand stark davon ab, wie gut Sie codieren können.
F # ist eine Ableitung von OCaml. Ich war überrascht, als ich herausfand, dass OCaml in der Finanzwelt, in der die Leistung bei der Eingabe von Zahlen sehr wichtig ist, häufig verwendet wird. Ich war weiter überrascht, als ich herausfand, dass OCaml eine der schnelleren Sprachen ist und eine Leistung aufweist, die der der schnellsten C- und C ++ - Compiler entspricht.
F # basiert auf der CLR . In der CLR wird Code in einer Form von Bytecode ausgedrückt, der als Common Intermediate Language bezeichnet wird. Als solches profitiert es von den Optimierungsfunktionen der JIT und hat eine Leistung, die mit C # vergleichbar ist (aber nicht unbedingt mit C ++), wenn der Code gut geschrieben ist.
CIL-Code kann vor der Laufzeit in einem separaten Schritt mithilfe des Native Image Generator (NGEN) zu nativem Code kompiliert werden. Dies beschleunigt alle späteren Ausführungen der Software, da die CIL-zu-Native-Kompilierung nicht mehr erforderlich ist.
Eine zu berücksichtigende Sache ist, dass funktionale Sprachen wie F # von einem deklarativeren Programmierstil profitieren. In gewisser Weise spezifizieren Sie die Lösung in wichtigen Sprachen wie C ++ zu stark, was die Optimierungsfähigkeit des Compilers einschränkt. Ein deklarativerer Programmierstil kann dem Compiler theoretisch zusätzliche Möglichkeiten zur algorithmischen Optimierung bieten.
quelle
Es hängt davon ab, welche Art von wissenschaftlichem Computing Sie betreiben.
Wenn Sie
traditional heavy computing
z. B. lineare Algebra oder verschiedene Optimierungen durchführen, sollten Sie Ihren Code nicht in das .NET-Framework einfügen, zumindest nicht für F # geeignet. Da dies auf Algorithmenebene erfolgt, müssen die meisten Algorithmen in einer zwingenden Sprache codiert werden, um eine gute Leistung in Bezug auf Laufzeit und Speichernutzung zu erzielen. Andere erwähnten parallel, ich muss sagen, es ist wahrscheinlich nutzlos, wenn Sie Low-Level-Sachen wie parallel eine SVD-Implementierung machen. Denn wenn Sie wissen, wie man eine SVD parallelisiert, werden Sie einfach keine Hochsprachen verwenden. Fortran, C oder modifiziertes C (z. B. Cilk ) sind Ihre Freunde.Ein Großteil des heutigen wissenschaftlichen Rechnens ist jedoch nicht von dieser Art, was eine Art von Anwendungen auf hoher Ebene ist, z. B. statistisches Rechnen und Data Mining. Bei diesen Aufgaben gibt es neben einer linearen Algebra oder Optimierung auch viele Datenflüsse, E / A-Vorgänge, Vorbesetzungen, Grafiken usw. Für diese Aufgaben ist F # sehr leistungsfähig, da es prägnant, funktional, sicher und einfach zu handhaben ist parallel usw.
Wie andere bereits erwähnt haben, unterstützt .Net Platform Invoke gut. Tatsächlich verwenden einige Projekte in MS .Net und P / Invoke zusammen, um die Leistung am Flaschenhals zu verbessern.
quelle
Ich glaube nicht, dass Sie leider viele verlässliche Informationen finden werden. F # ist immer noch eine sehr neue Sprache. Selbst wenn es ideal für leistungsintensive Workloads geeignet wäre, gäbe es nicht so viele Personen mit bedeutender Erfahrung, über die man berichten könnte. Darüber hinaus ist die Leistung nur sehr schwer genau einzuschätzen, und Mikrobenchmarks sind schwer zu verallgemeinern. Selbst in C ++ können Sie dramatische Unterschiede zwischen Compilern feststellen. Fragen Sie sich, ob F # mit einem C ++ - Compiler oder mit der hypothetischen "bestmöglichen" ausführbaren C ++ - Datei konkurrenzfähig ist ?
In Bezug auf bestimmte Benchmarks gegen C ++ sind hier einige möglicherweise relevante Links: O'Caml vs. F #: QR-Zerlegung ; F # vs Unmanaged C ++ für parallele Zahlen . Beachten Sie, dass der Autor als Autor von F # -bezogenem Material und als Anbieter von F # -Werkzeugen ein begründetes Interesse am Erfolg von F # hat. Nehmen Sie diese Behauptungen daher mit einem Körnchen Salz.
Ich denke, man kann mit Sicherheit sagen, dass es einige Anwendungen geben wird, bei denen F # hinsichtlich der Ausführungszeit wettbewerbsfähig ist, und wahrscheinlich einige andere, bei denen dies nicht der Fall ist. F # benötigt in den meisten Fällen wahrscheinlich mehr Speicher. Natürlich wird die ultimative Leistung auch stark von den Fähigkeiten des Programmierers abhängen - ich denke, F # wird mit ziemlicher Sicherheit eine produktivere Sprache für einen mäßig kompetenten Programmierer sein. Darüber hinaus denke ich, dass die CLR unter Windows derzeit auf den meisten Betriebssystemen für die meisten Aufgaben eine bessere Leistung als Mono aufweist, was sich auch auf Ihre Entscheidungen auswirken kann. Da F # wahrscheinlich einfacher zu parallelisieren ist als C ++, hängt es natürlich auch von der Art der Hardware ab, auf der Sie ausgeführt werden möchten.
Letztendlich denke ich, dass die einzige Möglichkeit, diese Frage wirklich zu beantworten, darin besteht, F # - und C ++ - Code zu schreiben, der für die Art der Berechnungen repräsentativ ist, die Sie ausführen und vergleichen möchten.
quelle
let f x y = (expensive x |> g) y
sich grundlegend vonlet f x = expensive x |> g
in F #, obwohl sie in einer funktionalen Welt semantisch äquivalent sind.Hier sind zwei Beispiele, die ich teilen kann:
Matrixmultiplikation: Ich habe einen Blog-Beitrag , in dem verschiedene Matrixmultiplikationsimplementierungen verglichen werden .
LBFGS
Ich habe einen großen logistischen Regressionslöser mit LBFGS-Optimierung, der in C ++ codiert ist. Die Implementierung ist gut abgestimmt. Ich habe Code in C ++ / CLI in Code geändert, dh den Code in .Net kompiliert. Die .Net-Version ist drei- bis fünfmal langsamer als die naiv kompilierte Version für verschiedene Datensätze. Wenn Sie LBFGS in F # codieren, kann die Leistung nicht besser sein als C ++ / CLI oder C # (wäre aber sehr nahe).
Ich habe einen weiteren Beitrag über Warum F # die Sprache für Data Mining ist , obwohl es nicht ganz mit dem Leistungsproblem zusammenhängt, das Sie hier betreffen, sondern mit wissenschaftlichem Rechnen in F #.
quelle
inline
F # besteht nicht darin, dass der Overhead von Funktionsaufrufen entfällt, sondern dass die CLR Ihren Code typspezialisiert. Wenn Ihr LBFGS nur Handlingfloat array
odervector
Ein- und Ausgänge ist, haben Sie es von Hand auf einen bestimmten Fall spezialisiert, und das hat es viel weniger nützlich gemacht. Eine universelle BFGS-Implementierung sollte ihre Eingabe lesen und ihre Ausgabe direkt in die Datenstrukturen des Benutzers schreiben, wobei Funktionen verwendet werden, die der Benutzer bereitstellt. F # hat hier einen großen Leistungsvorteil gegenüber C #.Wenn ich "in 2-3 Jahren noch einmal fragen" sage, wird das Ihre Frage meiner Meinung nach vollständig beantworten :-)
Erwarten Sie zunächst nicht, dass sich F # in perfekter Weise von C # unterscheidet, es sei denn, Sie führen absichtlich einige verschlungene Rekursionen durch, und ich würde vermuten, dass dies nicht der Fall ist, da Sie nach Zahlen gefragt haben.
Gleitkomma-weise ist es sicherlich besser als Java, da CLR nicht auf plattformübergreifende Einheitlichkeit abzielt, was bedeutet, dass JIT auf 80 Bit geht, wann immer es kann. Auf der anderen Seite haben Sie keine Kontrolle darüber, außer die Anzahl der Variablen zu beobachten, um sicherzustellen, dass genügend FP-Register vorhanden sind.
Wenn Sie laut genug schreien, passiert in 2-3 Jahren möglicherweise etwas, da Direct3D ohnehin als allgemeine API in .NET eingeht und der in XNA erstellte C # -Code auf Xbox ausgeführt wird, der dem Bare Metal, das Sie mit CLR erhalten können, so nahe kommt . Das bedeutet immer noch, dass Sie selbst einen Zwischencode benötigen.
Erwarten Sie also nicht, dass CUDA oder sogar die Fähigkeit, NVIDIA-Bibliotheken zu verknüpfen und loszulegen. Sie hätten viel mehr Glück, wenn Sie diesen Ansatz mit Haskell versuchen würden, wenn Sie aus irgendeinem Grund wirklich eine "funktionale" Sprache benötigen, da Haskell so konzipiert wurde, dass es aus reiner Notwendigkeit verlinkungsfreundlich ist.
Mono.Simd wurde bereits erwähnt, und obwohl es für CLR wieder portierbar sein sollte, könnte es eine ziemliche Arbeit sein, dies tatsächlich zu tun.
Es gibt ziemlich viel Code in einem social.msdn-Beitrag über die Verwendung von SSE3 in .NET, mit C ++ / CLI und C #, Array-Blitting, Einfügen von SSE3-Code für Perf usw.
Es wurde darüber gesprochen, CECIL auf kompiliertem C # auszuführen , um Teile in HLSL zu extrahieren, in Shader zu kompilieren und einen Klebercode zu verknüpfen, um dies zu planen (CUDA macht sowieso das Äquivalent), aber ich glaube nicht, dass daraus etwas ausführbares wird.
Eine Sache, die Ihnen mehr wert sein könnte, wenn Sie bald etwas ausprobieren möchten, ist PhysX.Net auf Codeplex . Erwarten Sie nicht, dass es einfach auspackt und die Magie ausübt. Allerdings hat ih derzeit einen aktiven Autor und der Code ist sowohl normales C ++ als auch C ++ / CLI. Yopu kann wahrscheinlich Hilfe vom Autor erhalten, wenn Sie auf Details eingehen und möglicherweise einen ähnlichen Ansatz für CUDA verwenden möchten. Für CUDA mit voller Geschwindigkeit müssen Sie immer noch Ihre eigenen Kernel kompilieren und dann einfach eine Schnittstelle zu .NET herstellen. Je einfacher dieser Teil ist, desto glücklicher werden Sie sein.
Es gibt eine CUDA.NET-Bibliothek , die kostenlos sein soll, aber die Seite enthält nur eine E-Mail-Adresse. Erwarten Sie also einige angehängte Zeichenfolgen, und während der Autor einen Blog schreibt, ist er nicht besonders gesprächig darüber, was in der Bibliothek enthalten ist.
Oh, und wenn Sie das Budget haben, könnten Sie diesem Psi Lambda einen Blick geben (KappaCUDAnet ist der .NET-Teil). Anscheinend werden sie die Preise im November erhöhen (wenn es kein Verkaufstrick ist :-)
quelle
Erstens ist C deutlich schneller als C ++. Wenn Sie also so viel Geschwindigkeit benötigen, sollten Sie die Bibliothek usw. in c erstellen.
In Bezug auf F # verwenden die meisten Benchmarks Mono, das teilweise aufgrund der Verwendung des Boehm-GC bis zu 2 * langsamer als MS CLR ist (sie haben einen neuen GC und LVVM, aber diese sind noch nicht ausgereift und unterstützen keine Generika usw.).
NEUE Sprachen selbst werden zu einem IR (CIL) kompiliert, das genauso effizient wie C ++ zu nativem Code kompiliert wird. Es gibt ein Problem, unter dem die meisten GC-Sprachen leiden, nämlich große Mengen veränderlicher Schreibvorgänge (dies schließt C ++ .NET ein, wie oben erwähnt). Und es gibt ein bestimmtes wissenschaftliches Problem, das dies erfordert. Diese sollten bei Bedarf wahrscheinlich eine native Bibliothek verwenden oder das Flyweight-Muster verwenden, um Objekte aus einem Pool wiederzuverwenden (wodurch Schreibvorgänge reduziert werden). Der Grund dafür ist, dass es in der .NET-CLR eine Schreibbarriere gibt, bei der beim Aktualisieren eines Referenzfelds (einschließlich eines Felds) in einer Tabelle ein Bit gesetzt wird, das besagt, dass diese Tabelle geändert wurde. Wenn Ihr Code aus vielen solchen Schreibvorgängen besteht, leidet er.
Das heißt, eine .NET-App wie C #, die viel statischen Code, Strukturen und Ref / Out für die Strukturen verwendet, kann eine C-ähnliche Leistung erzeugen, aber es ist sehr schwierig, so zu codieren oder den Code (wie C) zu pflegen.
Wo F # jedoch glänzt, ist Parralelismus über unveränderliche Daten, was mit mehr lesbasierten Problemen einhergeht. Es ist erwähnenswert, dass die meisten Benchmarks in veränderlichen Schreibvorgängen viel höher sind als in realen Anwendungen.
In Bezug auf Gleitkommazahlen sollten Sie eine alternative Bibliothek (dh die .Net-Bibliothek) zu den oCaml-Bibliotheken verwenden, da diese langsam ist. C / C ++ ermöglicht eine schnellere und niedrigere Genauigkeit, die oCaml standardmäßig nicht verwendet.
Zuletzt möchte ich argumentieren, dass eine Hochsprache wie C #, F # und eine ordnungsgemäße Profilerstellung für die gleiche Entwicklerzeit eine bessere Leistung als c und C ++ bieten. Wenn Sie einen Flaschenhals auf ac lib pinvoke call ändern, erhalten Sie auch eine C-ähnliche Leistung für kritische Bereiche. Das heißt, wenn Sie ein unbegrenztes Budget haben und mehr Wert auf Geschwindigkeit legen, ist Wartung der richtige Weg als C (nicht C ++).
quelle
Zuletzt wusste ich, dass die meisten wissenschaftlichen Berechnungen noch in FORTRAN durchgeführt wurden. Es ist immer noch schneller als alles andere für lineare Algebra-Probleme - nicht Java, nicht C, nicht C ++, nicht C #, nicht F #. LINPACK ist schön optimiert.
Die Bemerkung "Ihr Kilometerstand kann variieren" gilt jedoch für alle Benchmarks. Pauschalaussagen (außer meinen) sind selten wahr.
quelle