80 votes

Le C# est-il vraiment plus lent que le C++, par exemple ?

Cela fait un moment que je m'interroge sur cette question.

Bien sûr, certains éléments du C# ne sont pas optimisés pour la vitesse, et l'utilisation de ces objets ou de modifications du langage (comme LinQ) peut rendre le code plus lent.

Mais si vous n'utilisez aucun de ces ajustements, et que vous comparez simplement les mêmes morceaux de code en C# et C++ (il est facile de traduire l'un à l'autre). Sera-t-il vraiment beaucoup plus lent ?

J'ai vu des comparaisons qui montrent que le C# pourrait même être plus rapide dans certains cas, car en théorie le compilateur JIT devrait optimiser le code en temps réel et obtenir de meilleurs résultats :

Géré ou non géré ?

Il ne faut pas oublier que le compilateur JIT compile le code en temps réel, mais il s'agit d'une surcharge ponctuelle, le même code (une fois atteint et compilé) ne doit pas être compilé à nouveau au moment de l'exécution.

Le GC n'ajoute pas non plus beaucoup de surcharge, sauf si vous créez et détruisez des milliers d'objets (comme en utilisant String au lieu de StringBuilder). Et faire cela en C++ serait également coûteux.

Un autre point que je souhaite soulever est la meilleure communication entre les DLL introduite dans .Net. La plate-forme .Net communique beaucoup mieux que les DLL basées sur Managed COM.

Je ne vois pas de raison inhérente pour laquelle le langage devrait être plus lent, et je ne pense pas vraiment que C# soit plus lent que C++ (à la fois par expérience et par manque de bonne explication) .

Ainsi, un morceau du même code écrit en C# sera-t-il plus lent que le même code en C++ ?
Et si oui, alors POURQUOI ?

Une autre référence (qui en parle un peu, mais sans explication sur le POURQUOI) :

Pourquoi vouloir utiliser C# si c'est plus lent que C++ ?

0 votes

Parce que C# est beaucoup plus facile à utiliser que C++, surtout en ce qui concerne l'interface graphique.

0 votes

@Brain pouvez-vous expliquer "depends", je ne l'ai pas compris ?

3 votes

Vraiment... Ca dépend... Certaines choses sont plus rapides, d'autres plus lentes. Le C/C++ est plus "déterministe" (pas de ramasse-miettes dans le dos). Si vous voulez créer 100 threads, je peux vous dire que le GC vous hantera par sa lenteur (et avant de me dire que 100 threads sont trop nombreux, sachez que Skype et l'AV de McAfee sont chacun à 40 threads maintenant sur mon PC)... Marshaling en C# est une douleur (et c'est plus lent). Le codage est assez rapide. Non, ce n'est pas une attaque. Je préfère vraiment C#.

147voto

Jerry Coffin Points 237758

Avertissement : La question que vous avez posée est en réalité assez complexe - probablement beaucoup plus que vous ne le pensez. Par conséquent, ceci est un vraiment longue réponse.

D'un point de vue purement théorique, il y a probablement une réponse simple à cette question : il n'y a (probablement) rien dans C# qui l'empêche vraiment d'être aussi rapide que C++. En dépit de la théorie, il y a cependant quelques raisons pratiques pour lesquelles il est difficile d'utiliser C#. est plus lent pour certaines choses dans certaines circonstances.

Je me pencherai sur trois domaines fondamentaux de différences : les caractéristiques du langage, l'exécution par la machine virtuelle et le ramassage des déchets. Ces deux derniers points vont souvent de pair, mais peuvent être indépendants, c'est pourquoi je les aborderai séparément.

Caractéristiques de la langue

Le C++ accorde une grande importance aux modèles et aux fonctionnalités du système de modèles qui sont largement destinés à permettre que le plus de choses possibles soient faites au moment de la compilation, donc du point de vue du programme, ils sont "statiques". La métaprogrammation par gabarit permet d'effectuer des calculs complètement arbitraires au moment de la compilation (c'est-à-dire que le système de gabarit est complet de Turing). En tant que tel, tout ce qui ne dépend pas de l'entrée de l'utilisateur peut être calculé au moment de la compilation, de sorte qu'au moment de l'exécution, il s'agit simplement d'une constante. L'entrée peut toutefois inclure des choses comme des informations de type, de sorte qu'une grande partie de ce que vous feriez via la réflexion au moment de l'exécution en C# est normalement faite au moment de la compilation via la métaprogrammation de modèle en C++. Il existe cependant un compromis entre la vitesse d'exécution et la polyvalence : ce que les modèles peuvent faire, ils le font de manière statique, mais ils ne peuvent tout simplement pas faire tout ce que la réflexion peut faire.

Les différences entre les caractéristiques des langages signifient que presque toute tentative de comparaison des deux langages par simple translittération de C# en C++ (ou vice versa) est susceptible de produire des résultats se situant entre l'insignifiance et la tromperie (et la même chose serait vraie pour la plupart des autres paires de langages également). Le fait est que pour tout ce qui est plus grand que quelques lignes de code, presque personne n'est susceptible d'utiliser les langages de la même façon (ou suffisamment proche de la même façon) pour qu'une telle comparaison vous dise quoi que ce soit sur la façon dont ces langages fonctionnent dans la vie réelle.

Machine virtuelle

Comme presque toutes les VM raisonnablement modernes, celle de Microsoft pour .NET peut faire et fera une compilation JIT (alias "dynamique"). Cela représente toutefois un certain nombre de compromis.

Principalement, l'optimisation du code (comme la plupart des autres problèmes d'optimisation) est en grande partie un problème NP-complet. Pour tout ce qui n'est pas un programme vraiment trivial/jouet, il est presque garanti que vous n'allez pas vraiment "optimiser" le résultat (c'est-à-dire que vous ne trouverez pas le véritable optimum) -- l'optimiseur va simplement rendre le code meilleur qu'elle ne l'était auparavant. Cependant, un certain nombre d'optimisations bien connues prennent beaucoup de temps (et souvent de la mémoire) pour être exécutées. Avec un compilateur JIT, l'utilisateur attend pendant que le compilateur s'exécute. La plupart des techniques d'optimisation les plus coûteuses sont exclues. La compilation statique présente deux avantages : tout d'abord, si elle est lente (par exemple, lors de la construction d'un grand système), elle est généralement exécutée sur un serveur, et personne passe son temps à l'attendre. Deuxièmement, un exécutable peut être généré une fois et utilisé de nombreuses fois par de nombreuses personnes. La première minimise le coût de l'optimisation ; la seconde amortit le coût beaucoup plus faible sur un nombre beaucoup plus important d'exécutions.

Comme mentionné dans la question initiale (et sur de nombreux autres sites web), la compilation JIT offre la possibilité de mieux connaître l'environnement cible, ce qui devrait (au moins théoriquement) compenser cet avantage. Il ne fait aucun doute que ce facteur peut compenser au moins en partie l'inconvénient de la compilation statique. Pour quelques types de code et d'environnements cibles plutôt spécifiques, il peut l'emportent même sur les avantages de la compilation statique, parfois de façon assez spectaculaire. Cependant, du moins dans mes tests et mon expérience, c'est assez inhabituel. Les optimisations dépendantes de la cible semblent le plus souvent soit faire des différences assez faibles, soit ne pouvoir être appliquées (automatiquement, en tout cas) qu'à des types de problèmes assez spécifiques. Les cas évidents où cela se produirait seraient si vous exécutiez un programme relativement ancien sur une machine moderne. Un vieux programme écrit en C++ a probablement été compilé en code 32 bits, et continuera à utiliser du code 32 bits même sur un processeur moderne 64 bits. Un programme écrit en C# aurait été compilé en code octet, que la VM aurait ensuite compilé en code machine 64 bits. Si ce programme tirait un avantage substantiel de son exécution en code 64 bits, cela pourrait donner un avantage substantiel. Pendant une courte période où les processeurs 64 bits étaient relativement nouveaux, cela s'est produit assez souvent. Le code récent qui est susceptible de bénéficier d'un processeur 64 bits sera généralement disponible compilé statiquement en code 64 bits.

L'utilisation d'une VM permet également d'améliorer l'utilisation du cache. Les instructions d'une VM sont souvent plus compactes que les instructions machine natives. Un plus grand nombre d'entre elles peuvent tenir dans une quantité donnée de mémoire cache, de sorte que vous avez plus de chances qu'un code donné se trouve dans le cache au moment voulu. Cela peut aider à maintenir l'exécution interprétée du code VM plus compétitive (en termes de vitesse) que ce à quoi la plupart des gens s'attendraient au départ -- vous pouvez exécuter un code lot d'instructions sur un CPU moderne dans le temps pris par un cache manquée.

Il convient également de mentionner que ce facteur n'est pas nécessairement pas du tout de différence entre les deux. Rien n'empêche (par exemple) un compilateur C++ de produire un résultat destiné à être exécuté sur une machine virtuelle (avec ou sans JIT). En fait, le C++/CLI de Microsoft est presque qui -- un compilateur C++ (presque) conforme (bien qu'avec beaucoup d'extensions) qui produit une sortie destinée à fonctionner sur une machine virtuelle.

L'inverse est également vrai : Microsoft propose désormais .NET Native, qui compile le code C# (ou VB.NET) en un exécutable natif. Cela permet d'obtenir des performances généralement plus proches de celles du C++, tout en conservant les caractéristiques de C#/VB (par exemple, C# compilé en code natif supporte toujours la réflexion). Si vous avez du code C# à haute performance, cela peut être utile.

Collecte des ordures

D'après ce que j'ai vu, je dirais que le ramassage des déchets est le plus mal compris de ces trois facteurs. Juste pour un exemple évident, la question ici mentionne : "GC n'ajoute pas non plus beaucoup de surcharge, à moins que vous ne créiez et détruisiez des milliers d'objets [...]". En réalité, si vous créez y détruire des milliers d'objets, les frais généraux liés au ramasse-miettes seront généralement assez faibles. .NET utilise un ramasse-miettes générationnel, qui est une variété de collecteur de copies. Le ramasseur de déchets fonctionne en partant des "endroits" (par exemple, les registres et la pile d'exécution) où les pointeurs/références sont connu sous le nom de pour être accessible. Il "poursuit" ensuite ces pointeurs vers les objets qui ont été alloués sur le tas. Il examine ces objets à la recherche d'autres pointeurs/références, jusqu'à ce qu'il les ait tous suivis jusqu'à la fin des chaînes et qu'il ait trouvé tous les objets qui sont (au moins potentiellement) accessibles. Dans l'étape suivante, il prend tous les objets qui sont (ou au moins pourrait être ) en cours d'utilisation, et compacte le tas en les copiant tous dans un morceau contigu à une extrémité de la mémoire gérée dans le tas. Le reste de la mémoire est alors libre (sauf si les finaliseurs doivent être exécutés, mais au moins dans du code bien écrit, ils sont suffisamment rares pour que je les ignore pour le moment).

Ce que cela signifie, c'est que si vous créez et détruire beaucoup d'objets, le ramassage des ordures n'ajoute que très peu de surcharge. La durée d'un cycle de ramassage des déchets dépend presque entièrement du nombre d'objets créés, mais no détruit. La principale conséquence de la création et de la destruction d'objets à la hâte est simplement que la GC doit s'exécuter plus souvent, mais chaque cycle sera toujours rapide. Si vous créez des objets et Ne le fais pas. les détruire, le GC fonctionnera plus souvent y chaque cycle sera sensiblement plus lent car il passe plus de temps à rechercher des pointeurs vers des objets potentiellement vivants, y il passe plus de temps à copier les objets qui sont encore utilisés.

Pour lutter contre ce phénomène, la récupération générationnelle part du principe que les objets que ont qui sont restés "en vie" pendant un certain temps sont susceptibles de continuer à rester en vie pendant un certain temps encore. En se basant sur cela, il a un système où les objets qui survivent à un certain nombre de cycles de ramassage des ordures deviennent "titulaires", et le ramasseur d'ordures commence à simplement supposer qu'ils sont toujours utilisés, donc au lieu de les copier à chaque cycle, il les laisse simplement tranquilles. C'est une hypothèse valable assez souvent pour que le scavenging générationnel ait généralement une surcharge considérablement plus faible que la plupart des autres formes de GC.

La gestion "manuelle" de la mémoire est souvent tout aussi mal comprise. Pour ne citer qu'un exemple, de nombreuses tentatives de comparaison supposent que toute gestion manuelle de la mémoire suit également un modèle spécifique (par exemple, l'allocation la mieux adaptée). Cette hypothèse n'est souvent pas plus proche de la réalité (si tant est qu'elle le soit) que les croyances de nombreuses personnes concernant le ramassage des ordures (par exemple, l'hypothèse répandue selon laquelle il est normalement effectué en utilisant le comptage de références).

Compte tenu de la diversité des stratégies de collecte des déchets. y gestion manuelle de la mémoire, il est assez difficile de comparer les deux en termes de vitesse globale. Tenter de comparer la vitesse d'allocation et/ou de libération de la mémoire (en soi) est pratiquement garanti pour produire des résultats qui, au mieux, ne veulent rien dire et, au pire, sont carrément trompeurs.

Sujet bonus : Points de repère

Étant donné que de nombreux blogs, sites web, articles de magazines, etc., prétendent fournir des preuves "objectives" dans un sens ou dans l'autre, je vais également apporter mon grain de sel à ce sujet.

La plupart de ces benchmarks sont un peu comme des adolescents qui décident de faire une course de voitures, et celui qui gagne peut garder les deux voitures. Les sites Web diffèrent cependant sur un point crucial : le type qui publie le benchmark a le droit de conduire les deux voitures. Par un étrange hasard, sa voiture gagne toujours, et tous les autres doivent se contenter de "faites-moi confiance, j'étais ". vraiment en conduisant ta voiture aussi vite qu'elle peut aller."

Il est facile d'écrire un mauvais benchmark qui produit des résultats qui ne veulent presque rien dire. Quiconque possède les compétences nécessaires pour concevoir un benchmark qui produise quelque chose de significatif, possède également les compétences pour en produire un qui donnera les résultats qu'il a décidé de vouloir. En fait, c'est probablement plus facile d'écrire du code pour produire un résultat spécifique que du code qui produira réellement des résultats significatifs.

Comme l'a dit mon ami James Kanze, "ne faites jamais confiance à une référence que vous n'avez pas falsifiée vous-même".

Conclusion

Il n'y a pas de réponse simple. Je suis raisonnablement certain que je pourrais tirer à pile ou face pour choisir le gagnant, puis choisir un nombre entre (disons) 1 et 20 pour le pourcentage de victoire, et écrire un code qui ressemblerait à un benchmark raisonnable et juste, et qui produirait cette conclusion courue d'avance (au moins sur un processeur cible - un processeur différent pourrait changer un peu le pourcentage).

Comme d'autres l'ont souligné, pour le plus code, la vitesse est presque sans importance. Le corollaire de cela (qui est bien plus souvent ignoré) est que dans le peu de code où la vitesse est importante, elle est généralement d'une grande importance. lot . Au moins dans mon expérience, pour le code où cela compte vraiment, C++ est presque toujours le gagnant. Il y a certainement des facteurs qui favorisent C#, mais dans la pratique, ils semblent être plus importants que les facteurs qui favorisent C++. Vous pouvez certainement trouver des benchmarks qui indiqueront le résultat de votre choix, mais lorsque vous écrivez du code réel, vous pouvez presque toujours le rendre plus rapide en C++ qu'en C#. Cela peut (ou non) demander plus de compétences et/ou d'efforts d'écriture, mais c'est pratiquement toujours possible.

41voto

Adam Houldsworth Points 38632

Parce que vous n'avez pas toujours besoin d'utiliser la langue (et j'utilise ce terme au sens large) "la plus rapide" ? Je ne vais pas au travail en Ferrari juste parce que c'est plus rapide...

17 votes

Je pense que cette analogie est très pertinente. Si je conduisais une Ferrari pour me rendre au travail, je n'irais pas plus vite. La voiture est certainement capable d'aller plus vite que ce que je conduis, mais la capacité du véhicule n'est pas le facteur limitant. Il y a les limitations de vitesse, le trafic, l'état des routes, etc. De même, les logiciels sont généralement limités par des choses telles que l'interaction avec l'utilisateur, les E/S, etc. Le langage que vous utilisez ne fera pas une grande différence dans de tels cas.

3 votes

Mais la question était "Laquelle de ces voitures est une Ferrari, et laquelle est une Volkswagen ?" ou si les deux sont des Ferrari ou non.

0 votes

@pepr La question dans le titre est que oui, mais la question à la fin du post est "pourquoi voudriez-vous utiliser une Volkswagen quand vous pouvez utiliser une Ferrari ?".

22voto

Ofek Shilon Points 3170

En 2005, deux experts en performances de MS des deux côtés de la barrière natif/géré ont tenté de répondre à la même question. Leur méthode et leur processus sont toujours fascinants et leurs conclusions sont toujours valables aujourd'hui - et je ne connais pas de meilleure tentative pour donner une réponse éclairée. Ils ont noté qu'une discussion sur raisons possibles pour les différences de performance est hypothétique et futile, et une véritable discussion doit avoir une certaine base empirique pour l'impact de ces différences dans le monde réel.

Donc, le Ancien Nouveau Raymond Chen y Rico Mariani fixer les règles d'une compétition amicale. Un dictionnaire chinois/anglais a été choisi comme contexte d'application jouet : assez simple pour être codé comme un projet secondaire de loisir, mais assez complexe pour démontrer des modèles d'utilisation de données non triviaux. Les règles ont commencé par être simples - Raymond a codé une implémentation simple en C++, puis Rico l'a transférée en C#. ligne par ligne sans aucune sophistication, et les deux implémentations ont exécuté un benchmark. Ensuite, plusieurs itérations d'optimisations ont eu lieu.

Les détails complets sont ici : 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 .

Ce dialogue de titans est exceptionnellement instructif et je vous recommande vivement de vous y plonger - mais si vous manquez de temps ou de patience, Jeff Atwood a compilé les lignes de fond de manière magnifique :

enter image description here

Finalement, le C++ était 2x plus rapide - mais initialement, il était 13x plus lent.

En tant que Rico résume :

Alors, ai-je honte de ma défaite écrasante ? Pas du tout. Le code géré a obtenu un très bon résultat pour presque aucun effort. Pour battre la version gérée, Raymond a dû :

  • Écrire ses propres trucs de fichier/io

  • Écrire sa propre classe de cordes

  • Écrire son propre allocateur

  • Rédiger sa propre cartographie internationale

Bien sûr, il a utilisé les bibliothèques de niveau inférieur disponibles pour le faire, mais c'est quand même beaucoup de travail. Pouvez-vous appeler ce qu'il reste un programme STL ? Je ne pense pas.

C'est encore mon expérience, 11 ans et qui sait combien de versions de C#/C++ plus tard.

Ce n'est pas une coïncidence, bien sûr, car ces deux langages atteignent de manière spectaculaire leurs objectifs de conception très différents. Le C# veut être utilisé là où le coût de développement est la principale considération (encore la majorité des logiciels), et le C++ brille là où vous n'épargnez aucune dépense pour tirer la moindre performance de votre machine : jeux, algo-trading, centres de données, etc.

21voto

Maxime Rouiller Points 5987

Les C++ ont toujours un avantage pour la performance. Avec le C#, je n'ai pas à gérer la mémoire et j'ai littéralement des tonnes de ressources à ma disposition pour faire mon travail.

Ce que vous devez vous demander, c'est plutôt lequel vous fait gagner du temps. Les machines sont incroyablement puissantes aujourd'hui et la majeure partie de votre code doit être réalisée dans un langage qui vous permet d'obtenir le maximum de valeur en un minimum de temps.

S'il y a un traitement de base qui prend beaucoup trop de temps en C#, vous pourriez alors construire un C++ et l'interopérer avec C#.

Arrêtez de penser aux performances de votre code. Commencez à créer de la valeur.

14 votes

+1 pour "Arrêtez de penser à la performance de votre code. Commencez à créer de la valeur."

2 votes

Je suis d'accord sur la vitesse de développement en C#. C'est beaucoup plus intéressant dans de nombreux cas.

0 votes

Le développeur a besoin d'un bon choix pour chaque cas. Dans certains cas, C# ne répondra pas aux besoins. Supposons une application basée sur le calcul, telle que Convertisseur vidéo ou mixeur vidéo ou virtualisation 3D ou calcul de flux ou un jeu . Vous devez également tenir compte du matériel cible du projet. Certaines applications doivent être exécutées sur un matériel bas de gamme.

6voto

Alexandre C. Points 31758

C# est plus rapide que le C++. Plus rapide à écrire. Pour les temps d'exécution, rien ne vaut un profileur.

Mais le C# ne dispose pas d'autant de bibliothèques que le C++ peut s'interfacer facilement.

Et C# dépend fortement de Windows...

5 votes

C# nécessite un soutien beaucoup plus important de la part du compilateur, du système d'exploitation et des plates-formes. Une préoccupation majeure pour les systèmes embarqués.

0 votes

C# fonctionne non seulement sur Windows, mais aussi sur Mac, Linux, Android, iOS, ... voir microsoft.com/net/multiple-platform-support , xamarin.com/platform , mono-project.com/docs/about-mono/supported-platforms .

0 votes

@yoyo : C# fonctionne sur d'autres plateformes (et il y avait Mono sous Linux aussi), mais les bibliothèques que vous voulez utiliser (par exemple Windows Forms) ne le peuvent pas. La portabilité est un problème pour le développement de C#.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X