Wie schnell ist D im Vergleich zu C ++?
Mag ich einige Funktionen von D, wäre aber interessiert, wenn Sie kommen mit einer
Laufzeit Strafe?
Vergleichen, die ich implementiert ein einfaches Programm, das berechnet Skalare Produkte von vielen kurzen Vektoren in C++ und D. Das Ergebnis ist überraschend:
- D: 18.9 s [siehe unten für die endgültige Laufzeit]
- C++: 3.8 s
Ist C++ wirklich fast fünf mal so schnell, oder habe ich einen Fehler in der D
Programm?
Ich kompilierte C++ mit g++ -O3 (gcc-snapshot 2011-02-19) und D mit dmd -O - (dmd-2.052), die auf eine moderate jüngsten linux-desktop. Die Ergebnisse sind reproduzierbar über mehrere Läufe und standard-Abweichungen vernachlässigbar.
Hier die C++ - Programm:
#include <iostream>
#include <random>
#include <chrono>
#include <string>
#include <vector>
#include <array>
typedef std::chrono::duration<long, std::ratio<1, 1000>> millisecs;
template <typename _T>
long time_since(std::chrono::time_point<_T>& time) {
long tm = std::chrono::duration_cast<millisecs>( std::chrono::system_clock::now() - time).count();
time = std::chrono::system_clock::now();
return tm;
}
const long N = 20000;
const int size = 10;
typedef int value_type;
typedef long long result_type;
typedef std::vector<value_type> vector_t;
typedef typename vector_t::size_type size_type;
inline value_type scalar_product(const vector_t& x, const vector_t& y) {
value_type res = 0;
size_type siz = x.size();
for (size_type i = 0; i < siz; ++i)
res += x[i] * y[i];
return res;
}
int main() {
auto tm_before = std::chrono::system_clock::now();
//1. allocate and fill randomly many short vectors
vector_t* xs = new vector_t [N];
for (int i = 0; i < N; ++i) {
xs[i] = vector_t(size);
}
std::cerr << "allocation: " << time_since(tm_before) << " ms" << std::endl;
std::mt19937 rnd_engine;
std::uniform_int_distribution<value_type> runif_gen(-1000, 1000);
for (int i = 0; i < N; ++i)
for (int j = 0; j < size; ++j)
xs[i][j] = runif_gen(rnd_engine);
std::cerr << "random generation: " << time_since(tm_before) << " ms" << std::endl;
//2. compute all pairwise scalar products:
time_since(tm_before);
result_type avg = 0;
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
avg += scalar_product(xs[i], xs[j]);
avg = avg / N*N;
auto time = time_since(tm_before);
std::cout << "result: " << avg << std::endl;
std::cout << "time: " << time << " ms" << std::endl;
}
Und hier die D-version:
import std.stdio;
import std.datetime;
import std.random;
const long N = 20000;
const int size = 10;
alias int value_type;
alias long result_type;
alias value_type[] vector_t;
alias uint size_type;
value_type scalar_product(const ref vector_t x, const ref vector_t y) {
value_type res = 0;
size_type siz = x.length;
for (size_type i = 0; i < siz; ++i)
res += x[i] * y[i];
return res;
}
int main() {
auto tm_before = Clock.currTime();
//1. allocate and fill randomly many short vectors
vector_t[] xs;
xs.length = N;
for (int i = 0; i < N; ++i) {
xs[i].length = size;
}
writefln("allocation: %i ", (Clock.currTime() - tm_before));
tm_before = Clock.currTime();
for (int i = 0; i < N; ++i)
for (int j = 0; j < size; ++j)
xs[i][j] = uniform(-1000, 1000);
writefln("random: %i ", (Clock.currTime() - tm_before));
tm_before = Clock.currTime();
//2. compute all pairwise scalar products:
result_type avg = cast(result_type) 0;
for (int i = 0; i < N; ++i)
for (int j = 0; j < N; ++j)
avg += scalar_product(xs[i], xs[j]);
avg = avg / N*N;
writefln("result: %d", avg);
auto time = Clock.currTime() - tm_before;
writefln("scalar products: %i ", time);
return 0;
}
InformationsquelleAutor der Frage Lars | 2011-02-28
Du musst angemeldet sein, um einen Kommentar abzugeben.
Damit alle Optimierungen und deaktivieren Sie alle Sicherheits-checks, kompilieren Sie Ihre E-Programm mit den folgenden DMD-flags:
BEARBEITEN: ich habe versucht, Ihre Programme mit g++, dmd und gdc. dmd nicht hinterherhinken, sondern gdc erreicht die Leistung sehr nah zu g++. Die Kommandozeile, die ich verwendet wurde
gdmd -O -release -inline
(gdmd ist ein wrapper um die gdc, die akzeptiert dmd-Optionen).Blick auf das assembler-listing, wie es aussieht, weder dmd noch gdc inline
scalar_product
aber g++/gdc Tat emittieren MMX-Anweisungen, so dass Sie vielleicht auto-Vektorisieren der Schleife.InformationsquelleAutor der Antwort Vladimir Panteleev
Eine große Sache, verlangsamt D unten ist ein subpar garbage collection Implementierung. Benchmarks, die nicht stark stress der GC zeigen sehr ähnliche performance wie C und C++ - code kompiliert, mit dem gleichen compiler-backend. Benchmarks, die stark stress der GC wird sich zeigen, dass D eine abgrundtief. Seien Sie versichert, obwohl, das ist eine einzige (wenn auch schwere) Qualität-der-Umsetzung-Problem, nicht ein gebackenes-in-Garantie von Langsamkeit. Auch D bietet Ihnen die Möglichkeit zum opt-out von GC-und tune-Speicher-management in performance-kritischen bits, während es immer noch mit die weniger performance-kritische 95% Ihres Codes.
Habe ich setzen Sie etwas Mühe in die Verbesserung der GC-performance in letzter Zeit und die Ergebnisse waren ziemlich dramatisch, zumindest auf synthetische benchmarks. Hoffentlich werden diese änderungen integriert in eine der nächsten Versionen und mildern das Problem.
InformationsquelleAutor der Antwort dsimcha
Dies ist ein sehr lehrreicher thread, danke für all die Arbeit, die auf die OP und Helfer.
Einen Hinweis - dieser test ist nicht die Beurteilung der generellen Frage der Abstraktion/feature Strafe oder sogar der backend Qualität. Es konzentriert sich auf die praktisch eine Optimierung (loop-Optimierung). Ich denke, es ist fair zu sagen, dass der gcc-backend ist etwas mehr verfeinert als die dmd ' s, aber es wäre ein Irrtum anzunehmen, dass der Abstand zwischen Ihnen so groß für alle Aufgaben.
InformationsquelleAutor der Antwort Andrei Alexandrescu
Jeden Fall scheint wie eine Qualität-Implementierung Problem.
Ich lief einige tests mit dem OP-code und ein paar änderungen vorgenommen. Ich habe tatsächlich D geht schneller, für LDC/clang++, Betriebssystem auf der Annahme, dass arrays muss werden dynamisch zugewiesen (
xs
und zugehörige Skalare). Weiter unten finden Sie einige zahlen.Fragen für die OP
Ist es gewollt, dass die gleichen Samen verwendet werden, die für jede iteration von C++ und nicht so D?
Setup
Habe ich gezwickt die original-D-Quelle (als
Skalare.d
) ein, um die Portierbarkeit zwischen den Plattformen. Dieser Bestand nur aus einer änderung der Art der zahlen verwendet, um Zugriff auf und ändern Sie die Größe des arrays.Ich habe daraufhin folgende änderungen vorgenommen:
Verwendet
uninitializedArray
zu vermeiden Standard-inits für Skalare in-xs (wahrscheinlich den größten Unterschied machte). Dies ist wichtig, weil D normalerweise Standard-inits alles still, die C++ nicht.Ausgeklammert code ausdrucken und ersetzt
writefln
mitwriteln
^^
) anstelle der manuellen Vermehrung Letzte Schritt ist die Berechnung der durchschnittlichensize_type
ersetzt und entsprechend mit der neuenindex_type
alias...somit
scalar2.cpp
(pastebin):Nach der Prüfung
scalar2.d
(priorisiert Optimierung auf Geschwindigkeit), aus Neugier ersetzt habe ich die loops inmain
mitforeach
Entsprechungen, und nannte esscalar3.d
(pastebin):Den ich kompiliert habe, jede dieser Prüfungen mit einem LLVM-basierten compiler, da LDC-scheint die beste option für D Zusammenstellung in Bezug auf Leistung. Auf meinem x86_64 Arch Linux installation, die ich verwendet die folgenden Pakete:
clang 3.6.0-3
ldc 1:0.15.1-4
dtools 2.067.0-2
Ich habe folgende Befehle zum kompilieren:
clang++ scalar.cpp -o"scalar.cpp.exe" -std=c++11 -O3
rdmd --compiler=ldc2 -O3 -boundscheck=off <sourcefile>
Ergebnisse
Die Ergebnisse (screenshot von raw-Ausgabe in der Konsole) jeder version der Quelle wie folgt:
scalar.cpp
(original C++):C++ setzt den standard bei 2582 ms.
scalar.d
(modifiziert OP Quelle):Lief für ~2957 ms. Langsamer als die C++ - Implementierung, aber nicht zu viel.
scalar2.d
(index/Länge Typ ändern und uninitializedArray Optimierung):In anderen Worten, ~1860 ms. So weit dies ist in der Führung.
scalar3.d
(foreaches):~2182 ms ist langsamer als
scalar2.d
aber schneller als die C++ version.Abschluss
Mit den richtigen Optimierungen, die D Umsetzung ging eigentlich schneller als der äquivalente C++ - Implementierung mit dem LLVM-basierten Compiler zur Verfügung. Die aktuelle Lücke zwischen D-und C++ für die meisten Anwendungen scheint nur zu sein, basierend auf den Einschränkungen der aktuellen Implementierungen.
InformationsquelleAutor der Antwort Erich Gubler
dmd ist die Referenz-Implementierung der Sprache-und damit die meisten arbeiten in der frontend Fehler zu beheben, anstatt die Optimierung der backend.
"in" ist schneller in Ihren Fall verursachen Sie dynamische arrays sind Referenztypen. Mit ref-Sie führen eine andere Ebene der Dereferenzierung (die normalerweise verwendet wird, zu ändern, den array selbst und nicht nur den Inhalt).
Vektoren sind in der Regel umgesetzt mit Strukturen, wo const ref, macht absolut Sinn. Sehen smallptD vs. smallpt für einen real-world-Beispiel mit Lasten von Vektor-Operationen und Zufälligkeit.
Beachten Sie, dass 64-Bit kann auch einen Unterschied machen. Ich habe einmal verpasst, dass auf x64-gcc kompiliert 64-Bit-code, während dmd noch standardmäßig 32 (wird sich ändern, wenn die 64-Bit-codegen reift). Es war eine Bemerkenswerte Beschleunigung, die mit "dmd -m64 ...".
InformationsquelleAutor der Antwort Trass3r
Ob C++ oder D schneller ist wahrscheinlich sehr davon abhängig, was du tust. Ich würde denken, dass beim Vergleich von gut-geschrieben in C++ gut geschrieben, D-code, würden Sie in der Regel entweder von ähnlicher Geschwindigkeit, oder C++ wäre schneller, aber was die speziellen compiler verwaltet zu optimieren, könnte eine große Wirkung haben völlig abgesehen von der Sprache selbst.
Gibt es jedoch sind ein paar Fälle, in denen D eine gute chance gegen C++ für Geschwindigkeit. Die Hauptstraße, die in den Sinn kommt wäre die string-Verarbeitung. Dank D - array slicing capabalities, Streicher (und arrays im Allgemeinen) verarbeitet werden können viel schneller als Sie können leicht tun, in C++. Für D1, Tango ' s XML-Prozessor ist sehr schnelldie in Erster Linie durch D array slicing-Funktionen (und hoffentlich auch D2 haben eine ähnlich schnelle XML-parser einmal dem derzeit gearbeitet wird, für die Phobos wurde abgeschlossen). Also, letztlich, ob D-oder C++ - geht schneller, ist sehr davon abhängig, was du tust.
Nun, ich bin überrascht, dass Sie sehen, dass ein solcher Unterschied in der Geschwindigkeit in diesem besonderen Fall, aber es ist die Art von Sache, die ich erwarten würde, um zu verbessern, wie dmd verbessert. Mit gdc könnte liefern bessere Ergebnisse und würde wahrscheinlich ein näherer Vergleich der Sprache selbst (und nicht über das backend) angegeben, dass es gcc-basiert ist. Aber es würde mich nicht Wundern, wenn es gibt eine Reihe von Dingen, die getan werden könnte, um die Geschwindigkeit des Codes, dmd erzeugt. Ich glaube nicht, dass es viel Frage, gcc ist reifer als die dmd an dieser Stelle. Und code-Optimierungen sind eine der wichtigsten Früchte der code maturity.
Letztlich das, was zählt, ist, wie gut dmd führt für Ihre Anwendung zu finden, aber ich nicht einverstanden sind, auf jeden Fall wäre es schön zu wissen, wie gut C++ und D vergleichen im Allgemeinen. In der Theorie, Sie sollten so ziemlich das gleiche, aber es kommt wirklich auf die Umsetzung. Ich denke, dass eine umfassende Reihe von benchmarks, die erforderlich wäre, um wirklich zu testen, wie gut die beiden jetzt vergleichen jedoch.
InformationsquelleAutor der Antwort Jonathan M Davis
Schreiben Sie C-code D also so weit wie die ist schneller, es hängt von einer Menge Dinge:
Unterschiede in der ersten nicht fair zu ziehen. Die zweiten geben könnte C++ von Vorteil, da es, wenn überhaupt, weniger schwere features. Der Dritte ist der Spaß ein: D-code, die in einige Möglichkeiten, ist einfacher zu optimieren, da in der Regel einfacher zu verstehen ist. Auch es hat die Fähigkeit zu tun, einen hohen Grad der generativen Programmierung, dass man Dinge wie ausführliche und sich wiederholende, aber schnellen code geschrieben werden, in einer kürzeren Formen.
InformationsquelleAutor der Antwort BCS
Scheint wie eine Qualität der Umsetzung Problem. Zum Beispiel, hier ist, was ich habe getestet mit:
Mit
ManualInline
definiert bekommen ich 28 Sekunden, aber ohne Sie bekomme ich 32. Damit der compiler nicht selbst inlining diese einfache Funktion, die ich denke, es ist klar, dass es sein sollte.(Meine Befehlszeile ist
dmd -O -noboundscheck -inline -release ...
.)InformationsquelleAutor der Antwort GManNickG