Je vais commencer par être en désaccord avec une partie de la réponse acceptée (et bien votée) à cette question en déclarant:
Il y a en fait beaucoup de raisons pour lesquelles le code JITted fonctionnera plus lentement qu'un programme C ++ correctement optimisé (ou tout autre langage sans surcharge d'exécution), notamment:
les cycles de calcul consacrés au code JITting lors de l'exécution ne sont par définition pas disponibles pour une utilisation dans l'exécution du programme.
tous les chemins chauds dans le JITter seront en concurrence avec votre code pour le cache d'instructions et de données dans le CPU. Nous savons que le cache domine en termes de performances et que les langages natifs comme C ++ n'ont pas ce type de conflit, par définition.
le budget-temps d'un optimiseur au moment de l'exécution est nécessairement beaucoup plus contraint que celui d'un optimiseur au moment de la compilation (comme l'a souligné un autre intervenant)
Bottom line: En fin de compte, vous voulez presque certainement en mesure de créer une implémentation plus rapide en C ++ que vous ne le pourriez en C # .
Maintenant, cela dit, combien plus rapide n'est vraiment pas quantifiable, car il y a trop de variables: la tâche, le domaine du problème, le matériel, la qualité des implémentations et de nombreux autres facteurs. Vous devrez exécuter des tests sur votre scénario pour déterminer la différence de performances, puis décider s'il vaut la peine de l'effort et de la complexité supplémentaires.
C'est un sujet très long et complexe, mais je pense qu'il vaut la peine de mentionner pour être complet que l'optimiseur d'exécution de C # est excellent, et est capable d'effectuer certaines optimisations dynamiques lors de l'exécution qui ne sont tout simplement pas disponibles pour C ++ avec son temps de compilation ( optimiseur statique). Même avec cela, l'avantage est toujours profondément ancré dans la cour de l'application native, mais l'optimiseur dynamique est la raison du " presque qualificatif certainement" donné ci-dessus.
-
En termes de performances relatives, j'ai également été troublé par les chiffres et les discussions que j'ai vus dans certaines autres réponses, j'ai donc pensé que je ferais sonner la voix et, en même temps, apporterais un certain soutien aux déclarations que j'ai faites ci-dessus.
Une grande partie du problème avec ces benchmarks est que vous ne pouvez pas écrire de code C ++ comme si vous écriviez C # et espérez obtenir des résultats représentatifs (par exemple, effectuer des milliers d'allocations de mémoire en C ++ va vous donner des chiffres terribles.)
Au lieu de cela, j'ai écrit un code C ++ légèrement plus idiomatique et comparé au code C # @Wiory fourni. Les deux changements majeurs que j'ai apportés au code C ++ étaient:
1) utilisé vector :: reserve ()
2) aplati le tableau 2d à 1d pour obtenir une meilleure localité de cache (bloc contigu)
C # (.NET 4.6.1)
private static void TestArray()
{
const int rows = 5000;
const int columns = 9000;
DateTime t1 = System.DateTime.Now;
double[][] arr = new double[rows][];
for (int i = 0; i < rows; i++)
arr[i] = new double[columns];
DateTime t2 = System.DateTime.Now;
Console.WriteLine(t2 - t1);
t1 = System.DateTime.Now;
for (int i = 0; i < rows; i++)
for (int j = 0; j < columns; j++)
arr[i][j] = i;
t2 = System.DateTime.Now;
Console.WriteLine(t2 - t1);
}
Durée d'exécution (Release): Init: 124ms, Fill: 165ms
C ++ 14 (Clang v3.8 / C2)
#include <iostream>
#include <vector>
auto TestSuite::ColMajorArray()
{
constexpr size_t ROWS = 5000;
constexpr size_t COLS = 9000;
auto initStart = std::chrono::steady_clock::now();
auto arr = std::vector<double>();
arr.reserve(ROWS * COLS);
auto initFinish = std::chrono::steady_clock::now();
auto initTime = std::chrono::duration_cast<std::chrono::microseconds>(initFinish - initStart);
auto fillStart = std::chrono::steady_clock::now();
for(auto i = 0, r = 0; r < ROWS; ++r)
{
for (auto c = 0; c < COLS; ++c)
{
arr[i++] = static_cast<double>(r * c);
}
}
auto fillFinish = std::chrono::steady_clock::now();
auto fillTime = std::chrono::duration_cast<std::chrono::milliseconds>(fillFinish - fillStart);
return std::make_pair(initTime, fillTime);
}
Durée d'exécution (Release): Init: 398µs (oui, c'est microsecondes), Fill: 152ms
Temps d'exécution total: C #: 289 ms, C ++ 152 ms (environ 90% plus rapide)
Observations
La modification de l'implémentation C # en la même implémentation de tableau 1d a donné Init: 40ms, Fill: 171ms, Total: 211ms ( C ++ était toujours presque 40% plus rapide ).
Il est beaucoup plus difficile de concevoir et d'écrire du code "rapide" en C ++ que d'écrire du code "normal" dans les deux langues.
Il est (peut-être) étonnamment facile d'obtenir de mauvaises performances en C ++; nous l'avons vu avec des performances de vecteurs non réservés. Et il y a beaucoup de pièges comme celui-ci.
Les performances de C # sont plutôt étonnantes quand on considère tout ce qui se passe au moment de l'exécution. Et cette performance est relativement facile d'accès.
Plus de données anecdotiques comparant les performances de C ++ et C #: https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=gpp&lang2=csharpcore
L'essentiel est que C ++ vous donne beaucoup plus de contrôle sur les performances. Voulez-vous utiliser un pointeur? Une référence? Empiler la mémoire? Tas? Polymorphisme dynamique ou éliminer la surcharge d'exécution d'une table virtuelle avec polymorphisme statique (via des modèles / CRTP)? En C ++, vous devez ... euh, accéder à faire tous ces choix (et plus) vous, idéalement afin que vos meilleures adresses de solution du problème que vous vous attaquez.
Demandez-vous si vous voulez ou avez réellement besoin de ce contrôle, car même pour l'exemple trivial ci-dessus, vous pouvez voir que bien qu'il y ait une amélioration significative des performances, il nécessite un investissement plus profond pour y accéder.