Je crois comprendre que C / C ++ produit du code natif à exécuter sur une architecture de machine particulière. Inversement, des langages comme Java et C # s'exécutent sur une machine virtuelle qui fait abstraction de l'architecture native. Logiquement, il semblerait impossible pour Java ou C # d'égaler la vitesse de C ++ à cause de cette étape intermédiaire, mais on m'a dit que les derniers compilateurs ("hot spot") peuvent atteindre cette vitesse ou même la dépasser.
C'est peut-être plus une question de compilation qu'une question de langue, mais quelqu'un peut-il expliquer en anglais simple comment il est possible pour l'un de ces langages de machine virtuelle de fonctionner mieux qu'une langue native?
Réponses:
Généralement, C # et Java peuvent être tout aussi rapides ou plus rapides car le compilateur JIT - un compilateur qui compile votre IL la première fois qu'il est exécuté - peut effectuer des optimisations qu'un programme compilé C ++ ne peut pas car il peut interroger la machine. Il peut déterminer si la machine est Intel ou AMD; Pentium 4, Core Solo ou Core Duo; ou si prend en charge SSE4, etc.
Un programme C ++ doit être compilé au préalable généralement avec des optimisations mixtes afin qu'il fonctionne correctement sur toutes les machines, mais n'est pas optimisé autant qu'il pourrait l'être pour une seule configuration (processeur, jeu d'instructions, autre matériel).
De plus, certaines fonctionnalités du langage permettent au compilateur en C # et Java de faire des hypothèses sur votre code qui lui permettent d'optimiser certaines parties qui ne sont tout simplement pas sûres pour le compilateur C / C ++. Lorsque vous avez accès à des pointeurs, de nombreuses optimisations ne sont tout simplement pas sûres.
Java et C # peuvent également effectuer des allocations de tas plus efficacement que C ++ car la couche d'abstraction entre le garbage collector et votre code lui permet de faire toute sa compression de tas en une seule fois (opération assez coûteuse).
Maintenant, je ne peux pas parler pour Java sur ce point suivant, mais je sais que C # par exemple supprimera en fait les méthodes et les appels de méthode quand il sait que le corps de la méthode est vide. Et il utilisera ce type de logique dans tout votre code.
Donc, comme vous pouvez le voir, il y a de nombreuses raisons pour lesquelles certaines implémentations C # ou Java seront plus rapides.
Maintenant, tout cela dit, des optimisations spécifiques peuvent être faites en C ++ qui épateront tout ce que vous pourriez faire avec C #, en particulier dans le domaine graphique et à chaque fois que vous êtes proche du matériel. Les pointeurs font des merveilles ici.
Donc, selon ce que vous écrivez, j'irais avec l'un ou l'autre. Mais si vous écrivez quelque chose qui ne dépend pas du matériel (pilote, jeu vidéo, etc.), je ne m'inquiéterais pas des performances de C # (encore une fois, je ne peux pas parler de Java). Ça ira très bien.
Du côté Java, @Swati fait remarquer un bon article:
https://www.ibm.com/developerworks/library/j-jtp09275
la source
Compilateur JIT vs statique
Comme déjà dit dans les articles précédents, JIT peut compiler IL / bytecode en code natif au moment de l'exécution. Le coût de cela a été mentionné, mais pas à sa conclusion:
JIT a un énorme problème est qu'il ne peut pas tout compiler: la compilation JIT prend du temps, donc le JIT ne compilera que certaines parties du code, alors qu'un compilateur statique produira un binaire natif complet: pour certains types de programmes, le Le compilateur surpassera facilement le JIT.
Bien sûr, C # (ou Java, ou VB) est généralement plus rapide à produire une solution viable et robuste que C ++ (ne serait-ce que parce que C ++ a une sémantique complexe, et que la bibliothèque standard C ++, bien qu'intéressante et puissante, est assez médiocre par rapport à la version complète portée de la bibliothèque standard de .NET ou Java), donc généralement, la différence entre C ++ et .NET ou Java JIT ne sera pas visible pour la plupart des utilisateurs, et pour les binaires critiques, eh bien, vous pouvez toujours appeler le traitement C ++ depuis C # ou Java (même si ce type d'appels natifs peut être assez coûteux en soi) ...
Métaprogrammation C ++
Notez que généralement, vous comparez le code d'exécution C ++ avec son équivalent en C # ou Java. Mais C ++ a une fonctionnalité qui peut surpasser Java / C # dès la sortie de la boîte, à savoir la métaprogrammation des modèles: le traitement du code sera effectué au moment de la compilation (augmentant ainsi considérablement le temps de compilation), ce qui aboutira à zéro (ou presque) à l'exécution.
J'ai encore vu un effet réel sur cela (je n'ai joué qu'avec des concepts, mais à ce moment-là, la différence était en secondes d'exécution pour JIT et zéro pour C ++), mais cela vaut la peine d'être mentionné, à côté du fait que la métaprogrammation du modèle n'est pas banal......
Utilisation de la mémoire native C ++
C ++ a une utilisation de la mémoire différente de Java / C #, et donc, présente différents avantages / défauts.
Quelle que soit l'optimisation JIT, rien n'ira aussi vite que l'accès direct du pointeur à la mémoire (ignorons un instant les caches du processeur, etc.). Donc, si vous avez des données contiguës en mémoire, y accéder via des pointeurs C ++ (c'est-à-dire des pointeurs C ... Donnons à Caesar son dû) sera plus rapide qu'en Java / C #. Et C ++ a RAII, ce qui rend beaucoup de traitement beaucoup plus facile qu'en C # ou même en Java. C ++ n'a pas besoin
using
de définir la portée de l'existence de ses objets. Et C ++ n'a pas definally
clause. Ce n'est pas une erreur.:-)
Et malgré les structures de type primitif C #, les objets C ++ "sur la pile" ne coûteront rien en allocation et en destruction, et n'auront pas besoin de GC pour travailler dans un thread indépendant pour faire le nettoyage.
En ce qui concerne la fragmentation de la mémoire, les allocateurs de mémoire en 2008 ne sont pas les anciens allocateurs de mémoire de 1980 qui sont généralement comparés à un GC: l'allocation C ++ ne peut pas être déplacée en mémoire, c'est vrai, mais alors, comme sur un système de fichiers Linux: Qui a besoin d'un disque dur défragmenter lorsque la fragmentation ne se produit pas? L'utilisation du bon allocateur pour la bonne tâche devrait faire partie de la boîte à outils du développeur C ++. Maintenant, écrire des allocateurs n'est pas facile, et puis, la plupart d'entre nous ont de meilleures choses à faire, et pour la plupart des utilisateurs, RAII ou GC est plus que suffisant.
Maintenant, le modèle de mémoire devient un peu plus compliqué avec la montée en puissance de la technologie multicœur et multithreading. Dans ce domaine, je suppose que .NET a l'avantage, et Java, m'a-t-on dit, a tenu le dessus. Il est facile pour certains hackers "sur le métal nu" de louer son code "près de la machine". Mais maintenant, il est bien plus difficile de produire un meilleur assemblage à la main que de laisser le compilateur faire son travail. Pour C ++, le compilateur est généralement devenu meilleur que le hacker depuis une décennie. Pour C # et Java, c'est encore plus facile.
Pourtant, le nouveau standard C ++ 0x imposera un modèle de mémoire simple aux compilateurs C ++, ce qui standardisera (et simplifiera ainsi) le code multiprocesseur / parallèle / threading efficace en C ++, et rendra les optimisations plus faciles et plus sûres pour les compilateurs. Mais alors, nous verrons dans quelques années si ses promesses sont tenues.
C ++ / CLI et C # / VB.NET
Remarque: Dans cette section, je parle de C ++ / CLI, c'est-à-dire du C ++ hébergé par .NET, pas du C ++ natif.
La semaine dernière, j'ai suivi une formation sur l'optimisation .NET et j'ai découvert que le compilateur statique est de toute façon très important. Aussi important que JIT.
Le même code compilé en C ++ / CLI (ou son ancêtre, Managed C ++) pourrait être fois plus rapide que le même code produit en C # (ou VB.NET, dont le compilateur produit le même IL que C #).
Parce que le compilateur statique C ++ était bien meilleur pour produire du code déjà optimisé que celui de C #.
Par exemple, l'intégration de fonctions dans .NET est limitée aux fonctions dont le bytecode est inférieur ou égal à 32 octets de longueur. Ainsi, du code en C # produira un accesseur de 40 octets, qui ne sera jamais incorporé par le JIT. Le même code en C ++ / CLI produira un accesseur de 20 octets, qui sera intégré par le JIT.
Un autre exemple est les variables temporaires, qui sont simplement compilées par le compilateur C ++ tout en étant toujours mentionnées dans l'IL produit par le compilateur C #. L'optimisation de la compilation statique C ++ entraînera moins de code, autorisant ainsi une optimisation JIT plus agressive, encore une fois.
La raison en est supposée être le fait que le compilateur C ++ / CLI a profité des vastes techniques d'optimisation du compilateur natif C ++.
Conclusion
J'adore C ++.
Mais pour autant que je le vois, C # ou Java sont dans l'ensemble un meilleur pari. Non pas parce qu'ils sont plus rapides que C ++, mais parce que lorsque vous additionnez leurs qualités, ils finissent par être plus productifs, nécessitent moins de formation et ont des bibliothèques standard plus complètes que C ++. Et comme pour la plupart des programmes, leurs différences de vitesse (d'une manière ou d'une autre) seront négligeables ...
Modifier (06/06/2011)
Mon expérience sur C # /. NET
J'ai maintenant 5 mois de codage C # professionnel presque exclusif (ce qui s'ajoute à mon CV déjà plein de C ++ et Java, et une touche de C ++ / CLI).
J'ai joué avec WinForms (Ahem ...) et WCF (cool!), Et WPF (Cool !!!! À la fois via XAML et C # brut. WPF est si facile que je pense que Swing ne peut tout simplement pas se comparer), et C # 4.0.
La conclusion est que s'il est plus facile / plus rapide de produire un code qui fonctionne en C # / Java qu'en C ++, il est beaucoup plus difficile de produire un code fort, sûr et robuste en C # (et encore plus difficile en Java) qu'en C ++. Les raisons abondent, mais elles peuvent être résumées par:
using
n'est pas aussi facile et puissant car écrire une implémentation Dispose correcte est difficile )readonly
et Java nefinal
sont nulle part aussi utiles que ceux de C ++const
(il n'y a aucun moyen d'exposer des données complexes en lecture seule (un arbre de nœuds, par exemple) en C # sans travail énorme, alors qu'il s'agit d'une fonctionnalité intégrée de C ++. Les données immuables sont une solution intéressante , mais tout ne peut pas être rendu immuable, donc ce n'est même pas suffisant, de loin ).Ainsi, C # reste un langage agréable tant que vous voulez quelque chose qui fonctionne, mais un langage frustrant au moment où vous voulez quelque chose qui fonctionne toujours et en toute sécurité .
Java est encore plus frustrant, car il a les mêmes problèmes que C #, et plus encore: manquant l'équivalent du
using
mot-clé de C # , un de mes collègues très expérimentés a passé trop de temps à s'assurer que ses ressources étaient correctement libérées, alors que l'équivalent en C ++ aurait été facile (en utilisant des destructeurs et des pointeurs intelligents).Donc, je suppose que le gain de productivité de C # / Java est visible pour la plupart du code ... jusqu'au jour où vous avez besoin que le code soit aussi parfait que possible. Ce jour-là, vous connaîtrez la douleur. (vous ne croirez pas ce qui est demandé à notre serveur et à nos applications GUI ...).
À propos de Java et C ++ côté serveur
J'ai gardé le contact avec les équipes serveurs (j'ai travaillé 2 ans parmi elles, avant de retourner dans l'équipe GUI), de l'autre côté du bâtiment, et j'ai appris quelque chose d'intéressant.
Les années dernières, la tendance était que les applications serveur Java soient destinées à remplacer les anciennes applications serveur C ++, car Java a beaucoup de frameworks / outils, et est facile à maintenir, déployer, etc. etc.
... Jusqu'à ce que le problème de la faible latence ne fasse son apparition ces derniers mois. Ensuite, les applications serveur Java, quelle que soit l'optimisation tentée par notre équipe Java qualifiée, ont simplement et proprement perdu la course contre l'ancien serveur C ++ pas vraiment optimisé.
Actuellement, la décision est de conserver les serveurs Java pour une utilisation courante où les performances, bien que toujours importantes, ne sont pas concernées par la cible à faible latence, et d'optimiser de manière agressive les applications de serveur C ++ déjà plus rapides pour les besoins à faible latence et ultra-faible latence.
Conclusion
Rien n'est aussi simple que prévu.
Java, et encore plus C #, sont des langages sympas, avec des bibliothèques et des frameworks standard étendus, où vous pouvez coder rapidement et obtenir des résultats très bientôt.
Mais lorsque vous avez besoin de puissance brute, d'optimisations puissantes et systématiques, d'une prise en charge solide du compilateur, de fonctionnalités de langage puissantes et d'une sécurité absolue, Java et C # rendent difficile la victoire des derniers pourcentages de qualité manquants mais critiques dont vous avez besoin pour rester au-dessus de la concurrence.
C'est comme si vous aviez besoin de moins de temps et de développeurs moins expérimentés en C # / Java qu'en C ++ pour produire du code de qualité moyenne, mais d'un autre côté, au moment où vous aviez besoin d'un code de qualité excellente pour perfectionner, il était soudainement plus facile et plus rapide d'obtenir les résultats directement en C ++.
Bien sûr, c'est ma propre perception, peut-être limitée à nos besoins spécifiques.
Mais encore, c'est ce qui se passe aujourd'hui, à la fois dans les équipes GUI et les équipes côté serveur.
Bien sûr, je mettrai à jour ce post si quelque chose de nouveau se produit.
Modifier (22/06/2011)
Sources:
Modifier (20/09/2011)
Sources:
la source
Chaque fois que je parle de performances gérées et non gérées, j'aime évoquer la série que Rico (et Raymond) a faite en comparant les versions C ++ et C # d'un dictionnaire chinois / anglais. Cette recherche Google vous permettra de lire par vous-même, mais j'aime le résumé de Rico.
Pour moi, l'essentiel est qu'il a fallu 6 révisions pour que la version non gérée batte la version gérée qui était un simple portage du code non géré d'origine. Si vous avez besoin de toutes les dernières performances (et que vous avez le temps et l'expertise pour l'obtenir), vous devrez rester non géré, mais pour moi, je profiterai de l'ordre de grandeur que j'ai sur les premières versions par rapport aux 33 % Je gagne si j'essaye 6 fois.
la source
La compilation pour des optimisations spécifiques du processeur est généralement surfaite. Il suffit de prendre un programme en C ++ et de le compiler avec optimisation pour pentium PRO et de l'exécuter sur un pentium 4. Puis recompiler avec Optimize pour pentium 4. J'ai passé de longs après-midi à le faire avec plusieurs programmes. Résultats généraux ?? Augmentation des performances généralement inférieure à 2-3%. Les avantages théoriques du JIT sont donc presque inexistants. La plupart des différences de performances ne peuvent être observées que lors de l'utilisation de fonctionnalités de traitement de données scalaires, ce qui nécessitera éventuellement un réglage fin manuel pour atteindre de toute façon des performances maximales. Les optimisations de ce type sont lentes et coûteuses à réaliser, ce qui les rend parfois inappropriées pour JIT de toute façon.
Dans le monde réel et dans les applications réelles, le C ++ est toujours généralement plus rapide que Java, principalement en raison d'une empreinte mémoire plus légère qui se traduit par de meilleures performances de cache.
Mais pour utiliser toutes les fonctionnalités C ++, le développeur doit travailler dur. Vous pouvez obtenir des résultats supérieurs, mais vous devez utiliser votre cerveau pour cela. C ++ est un langage qui a décidé de vous présenter plus d'outils, facturant le prix que vous devez les apprendre pour pouvoir bien utiliser le langage.
la source
JIT (Just In Time Compiling) peut être incroyablement rapide car il s'optimise pour la plate-forme cible.
Cela signifie qu'il peut tirer parti de n'importe quelle astuce du compilateur que votre processeur peut prendre en charge, quel que soit le processeur sur lequel le développeur a écrit le code.
Le concept de base du .NET JIT fonctionne comme ceci (fortement simplifié):
Appel d'une méthode pour la première fois:
Appel d'une méthode pour la deuxième fois:
Comme vous pouvez le voir, la deuxième fois, c'est pratiquement le même processus que C ++, sauf avec l'avantage des optimisations en temps réel.
Cela dit, il existe encore d'autres problèmes de frais généraux qui ralentissent un langage géré, mais le JIT aide beaucoup.
la source
J'aime la réponse d' Orion Adrian , mais il y a un autre aspect à cela.
La même question a été posée il y a des décennies à propos du langage d'assemblage par rapport aux langages «humains» comme FORTRAN. Et une partie de la réponse est similaire.
Oui, un programme C ++ est capable d'être plus rapide que C # sur n'importe quel algorithme donné (non trivial?), Mais le programme en C # sera souvent aussi rapide ou plus rapide qu'une implémentation "naïve" en C ++, et une version optimisée en C ++ prendra plus de temps à se développer et pourrait encore battre la version C # par une très petite marge. Alors, ça vaut vraiment le coup?
Vous devrez répondre à cette question une par une.
Cela dit, je suis un fan de longue date du C ++, et je pense que c'est un langage incroyablement expressif et puissant - parfois sous-estimé. Mais dans de nombreux problèmes «réels» (pour moi personnellement, cela signifie «le genre de problèmes pour lesquels je suis payé»), C # fera le travail plus tôt et plus sûr.
La plus grosse pénalité que vous payez? De nombreux programmes .NET et Java sont des porcs de mémoire. J'ai vu des applications .NET et Java prendre "des centaines" de mégaoctets de mémoire, alors que des programmes C ++ de complexité similaire effleurent à peine les "dizaines" de Mo.
la source
Je ne sais pas à quelle fréquence vous constaterez que le code Java fonctionnera plus rapidement que C ++, même avec Hotspot, mais je vais essayer d'expliquer comment cela pourrait arriver.
Considérez le code Java compilé comme un langage machine interprété pour la JVM. Lorsque le processeur Hotspot remarque que certaines parties du code compilé vont être utilisées plusieurs fois, il effectue une optimisation sur le code machine. Étant donné que l'assemblage de réglage manuel est presque toujours plus rapide que le code compilé C ++, il est normal de comprendre que le code machine réglé par programme ne le sera pas trop mauvais.
Donc, pour le code très répétitif, j'ai pu voir où il serait possible pour Hotspot JVM d'exécuter Java plus rapidement que C ++ ... jusqu'à ce que le ramasse-miettes entre en jeu. :)
la source
Since hand-tuning Assembly is almost always faster than C++ compiled code
? Qu'entendez-vous par «assemblage de réglage manuel» et «code compilé C ++»?En règle générale, l' algorithme de votre programme sera beaucoup plus important pour la vitesse de votre application que le langue . Vous pouvez implémenter un algorithme médiocre dans n'importe quel langage, y compris C ++. Dans cet esprit, vous serez généralement en mesure d'écrire du code plus rapidement dans un langage qui vous aide à implémenter un algorithme plus efficace.
Les langages de niveau supérieur y parviennent très bien en offrant un accès plus facile à de nombreuses structures de données pré-construites efficaces et en encourageant les pratiques qui vous aideront à éviter le code inefficace. Bien sûr, ils peuvent parfois aussi faciliter l'écriture d'un tas de code très lent, vous devez donc toujours connaître votre plate-forme.
De plus, C ++ rattrape les "nouvelles" fonctionnalités (notez les guillemets) comme les conteneurs STL, les pointeurs automatiques, etc. - voir la bibliothèque boost, par exemple. Et vous pourriez parfois constater que le moyen le plus rapide d'accomplir une tâche nécessite une technique comme l'arithmétique des pointeurs qui est interdite dans un langage de niveau supérieur - bien qu'ils vous permettent généralement d'appeler une bibliothèque écrite dans un langage qui peut l'implémenter comme vous le souhaitez. .
L'essentiel est de connaître le langage que vous utilisez, son API associée, ce qu'elle peut faire et ses limites.
la source
Je ne sais pas non plus ... mes programmes Java sont toujours lents. :-) Je n'ai cependant jamais vraiment remarqué que les programmes C # étaient particulièrement lents.
la source
Voici une autre référence intéressante, que vous pouvez essayer vous-même sur votre propre ordinateur.
Il compare ASM, VC ++, C #, Silverlight, applet Java, Javascript, Flash (AS3)
Démo de la vitesse du plugin Roozz
Veuillez noter que la vitesse du javascript varie beaucoup en fonction du navigateur qui l'exécute. Il en va de même pour Flash et Silverlight car ces plugins fonctionnent dans le même processus que le navigateur d'hébergement. Mais le plugin Roozz exécute des fichiers .exe standard, qui s'exécutent dans leur propre processus, donc la vitesse n'est pas influencée par le navigateur d'hébergement.
la source
Vous devez définir "performer mieux que ..". Eh bien, je sais, vous avez posé des questions sur la vitesse, mais ce n'est pas tout ce qui compte.
Et ainsi de suite, c'est biaisé, oui;)
Avec C # et Java, vous payez un prix pour ce que vous obtenez (codage plus rapide, gestion automatique de la mémoire, grande bibliothèque, etc.). Mais vous n'avez pas beaucoup de place pour marchander les détails: prenez le package complet ou rien.
Même si ces langages peuvent optimiser certains codes pour s'exécuter plus rapidement que le code compilé, toute l'approche est (à mon humble avis) inefficace. Imaginez conduire tous les jours 8 km jusqu'à votre lieu de travail, avec un camion! C'est confortable, ça fait du bien, vous êtes en sécurité (zone de déformation extrême) et après avoir appuyé sur l'accélérateur pendant un certain temps, elle sera même aussi rapide qu'une voiture standard! Pourquoi n'avons-nous pas tous un camion pour nous rendre au travail? ;)
En C ++, vous en avez pour votre argent, ni plus, ni moins.
Citant Bjarne Stroustrup: "C ++ est mon langage de récupération de place préféré car il génère si peu de déchets" lien texte
la source
times
sur un shell. Pour qu'il vérifie l'ensemble du programme, pas seulement un seul aspect. Les résultats sont-ils donc similaires?Le code exécutable produit à partir d'un compilateur Java ou C # n'est pas interprété - il est compilé en code natif "juste à temps" (JIT). Ainsi, le premier code temporel d'un programme Java / C # est rencontré pendant l'exécution, il y a une surcharge car le "compilateur d'exécution" (aussi appelé compilateur JIT) transforme le code d'octet (Java) ou le code IL (C #) en instructions machine natives. Cependant, la prochaine fois que ce code est rencontré alors que l'application est toujours en cours d'exécution, le code natif est exécuté immédiatement. Cela explique comment certains programmes Java / C # semblent être lents au départ, mais fonctionnent mieux au fur et à mesure qu'ils s'exécutent. Un bon exemple est un site Web ASP.Net. La toute première fois que vous accédez au site Web, cela peut être un peu plus lent car le code C # est compilé en code natif par le compilateur JIT.
la source
Quelques bonnes réponses ici sur la question spécifique que vous avez posée. J'aimerais prendre du recul et regarder la situation dans son ensemble.
Gardez à l'esprit que la perception de votre utilisateur de la vitesse du logiciel que vous écrivez est affectée par de nombreux autres facteurs que l'optimisation du codegen. Voici quelques exemples:
La gestion manuelle de la mémoire est difficile à faire correctement (pas de fuite), et encore plus difficile à faire efficacement (libérer de la mémoire peu de temps après que vous en ayez terminé). L'utilisation d'un GC est, en général, plus susceptible de produire un programme qui gère bien la mémoire. Êtes-vous prêt à travailler très dur et à retarder la livraison de votre logiciel, dans le but de surpasser le GC?
Mon C # est plus facile à lire et à comprendre que mon C ++. J'ai également plus de moyens de me convaincre que mon code C # fonctionne correctement. Cela signifie que je peux optimiser mes algorithmes avec moins de risque d'introduire des bogues (et les utilisateurs n'aiment pas les logiciels qui plantent, même s'ils le font rapidement!)
Je peux créer mon logiciel plus rapidement en C # qu'en C ++. Cela libère du temps pour travailler sur les performances et toujours livrer mon logiciel à temps.
Il est plus facile d'écrire une bonne interface utilisateur en C # qu'en C ++, donc je suis plus susceptible de pouvoir pousser le travail en arrière-plan pendant que l'interface utilisateur reste réactive, ou de fournir une interface utilisateur de progression ou de rythme lorsque le programme doit bloquer pendant un certain temps. Cela ne rend rien plus rapide, mais cela rend les utilisateurs plus heureux d'attendre.
Tout ce que j'ai dit sur C # est probablement vrai pour Java, je n'ai tout simplement pas l'expérience pour le dire avec certitude.
la source
Si vous êtes un programmeur Java / C # apprenant C ++, vous serez tenté de continuer à penser en termes de Java / C # et de traduire textuellement en syntaxe C ++. Dans ce cas, vous n'obtenez que les avantages mentionnés précédemment du code natif par rapport à interprété / JIT. Pour obtenir le plus grand gain de performances en C ++ par rapport à Java / C #, vous devez apprendre à penser en C ++ et concevoir du code spécifiquement pour exploiter les atouts du C ++.
Pour paraphraser Edsger Dijkstra : [votre langue maternelle] mutile l'esprit au-delà de la guérison.
Pour paraphraser Jeff Atwood : vous pouvez écrire [votre langue maternelle] dans n'importe quelle nouvelle langue.
la source
L'une des optimisations JIT les plus importantes est l'intégration de méthodes. Java peut même intégrer des méthodes virtuelles s'il peut garantir l'exactitude d'exécution. Ce type d'optimisation ne peut généralement pas être effectué par des compilateurs statiques standard car il nécessite une analyse de l'ensemble du programme, ce qui est difficile à cause d'une compilation séparée (en revanche, JIT a tout le programme disponible). L'intégration de méthodes améliore d'autres optimisations, donnant des blocs de code plus volumineux à optimiser.
L'allocation de mémoire standard en Java / C # est également plus rapide et la désallocation (GC) n'est pas beaucoup plus lente, mais seulement moins déterministe.
la source
free
etdelete
ne sont pas non plus déterministes et GC peut être rendu déterministe en n'allouant pas.Il est peu probable que les langages de la machine virtuelle surpassent les langages compilés, mais ils peuvent être suffisamment proches pour que cela n'ait pas d'importance, pour (au moins) les raisons suivantes (je parle pour Java ici car je n'ai jamais fait C #).
1 / L'environnement d'exécution Java est généralement capable de détecter des morceaux de code qui sont exécutés fréquemment et d'effectuer une compilation juste à temps (JIT) de ces sections afin qu'à l'avenir, elles s'exécutent à la vitesse de compilation complète.
2 / De vastes portions des bibliothèques Java sont compilées pour que, lorsque vous appelez une fonction de bibliothèque, vous exécutez du code compilé, non interprété. Vous pouvez voir le code (en C) en téléchargeant OpenJDK.
3 / Sauf si vous faites des calculs massifs, la plupart du temps, votre programme est en cours d'exécution, il attend l'entrée d'un humain très lent (relativement parlant).
4 / Etant donné qu'une grande partie de la validation du bytecode Java est effectuée au moment du chargement de la classe, la surcharge normale des vérifications d'exécution est considérablement réduite.
5 / Dans le pire des cas, du code gourmand en performances peut être extrait vers un module compilé et appelé depuis Java (voir JNI) pour qu'il s'exécute à pleine vitesse.
En résumé, le bytecode Java ne surpassera jamais le langage machine natif, mais il existe des moyens de l'atténuer. Le gros avantage de Java (comme je le vois) est la bibliothèque standard ÉNORME et la nature multiplateforme.
la source
Orion Adrian , permettez-moi d'inverser votre message pour voir à quel point vos remarques sont infondées, car on peut également dire beaucoup de choses sur C ++. Et dire que le compilateur Java / C # optimise les fonctions vides vous donne vraiment l'impression que vous n'êtes pas mon expert en optimisation, car a) pourquoi un vrai programme devrait-il contenir des fonctions vides, à l'exception du très mauvais code hérité, b) ce n'est vraiment pas optimisation du noir et de la fine pointe.
En dehors de cette phrase, vous avez dénoncé ouvertement les pointeurs, mais les objets en Java et C # ne fonctionnent-ils pas essentiellement comme des pointeurs C ++? Ne peuvent-ils pas se chevaucher? Ne peuvent-ils pas être nuls? C (et la plupart des implémentations C ++) a le mot clé restrict, les deux ont des types valeur, C ++ a une référence à valeur avec une garantie non nulle. Que proposent Java et C #?
>>>>>>>>>>
En règle générale, C et C ++ peuvent être tout aussi rapides ou plus rapides car le compilateur AOT - un compilateur qui compile votre code avant le déploiement, une fois pour toutes, sur votre serveur de compilation à mémoire élevée - peut faire des optimisations qu'un programme compilé C # ne peut pas parce qu'il a une tonne de temps pour le faire. Le compilateur peut déterminer si la machine est Intel ou AMD; Pentium 4, Core Solo ou Core Duo; ou si prend en charge SSE4, etc., et si votre compilateur ne prend pas en charge la répartition d'exécution, vous pouvez résoudre cela vous-même en déployant une poignée de binaires spécialisés.
Le programme AC # est généralement compilé lors de son exécution afin qu'il fonctionne correctement sur toutes les machines, mais n'est pas optimisé autant qu'il pourrait l'être pour une seule configuration (processeur, jeu d'instructions, autre matériel), et il doit passer un certain temps première. Des fonctionnalités telles que la fission de boucle, l'inversion de boucle, la vectorisation automatique, l'optimisation de l'ensemble du programme, l'expansion du modèle, l'introduction en bourse et bien d'autres sont très difficiles à résoudre complètement et complètement d'une manière qui n'ennuie pas l'utilisateur final.
De plus, certaines fonctionnalités du langage permettent au compilateur en C ++ ou C de faire des hypothèses sur votre code qui lui permettent d'optimiser certaines parties qui ne sont tout simplement pas sûres pour le compilateur Java / C #. Lorsque vous n'avez pas accès à l'ID de type complet des génériques ou à un flux de programme garanti, de nombreuses optimisations ne sont tout simplement pas sûres.
De plus, C ++ et C effectuent de nombreuses allocations de pile à la fois avec une seule incrémentation de registre, ce qui est sûrement plus efficace que les allocations Javas et C # en ce qui concerne la couche d'abstraction entre le garbage collector et votre code.
Maintenant, je ne peux pas parler pour Java sur ce point suivant, mais je sais que les compilateurs C ++ par exemple supprimeront en fait les méthodes et les appels de méthode quand il sait que le corps de la méthode est vide, il éliminera les sous-expressions courantes, il peut essayer de réessayer pour trouver une utilisation optimale du registre, il n'applique pas la vérification des limites, il va autovectoriser les boucles et les boucles internes et inversera l'intérieur vers l'extérieur, il déplace les conditions hors des boucles, il divise et déverrouille les boucles. Il étendra std :: vector dans des tableaux de surcharge natifs comme vous le feriez en C. Il fera des optimisations inter-procédurales. Il construira des valeurs de retour directement sur le site de l'appelant. Il pliera et propagera les expressions. Il réorganisera les données de manière conviviale pour le cache. Cela fera des threads sautés. Il vous permet d'écrire des traceurs de rayons au moment de la compilation avec une surcharge d'exécution nulle. Cela fera des optimisations basées sur des graphiques très coûteuses. Cela réduira la force, s'il remplaçait certains codes par du code syntaxiquement totalement inégal mais sémantiquement équivalent (l'ancien "xor foo, foo" est juste l'optimisation la plus simple, bien que dépassée, de ce type). Si vous le demandez gentiment, vous pouvez omettre les normes de virgule flottante IEEE et activer encore plus d'optimisations comme la réorganisation des opérandes en virgule flottante. Une fois qu'il a massé et massacré votre code, il peut répéter tout le processus, car souvent, certaines optimisations jettent les bases d'optimisations encore plus certaines. Il peut également simplement réessayer avec des paramètres mélangés et voir comment l'autre variante se classe dans son classement interne. Et il utilisera ce type de logique dans tout votre code. s'il remplace certains codes par du code syntaxiquement totalement inégal mais sémantiquement équivalent (l'ancien "xor foo, foo" est simplement l'optimisation la plus simple, bien que dépassée, de ce type). Si vous le demandez gentiment, vous pouvez omettre les normes de virgule flottante IEEE et activer encore plus d'optimisations comme la réorganisation des opérandes en virgule flottante. Une fois qu'il a massé et massacré votre code, il peut répéter tout le processus, car souvent, certaines optimisations jettent les bases d'optimisations encore plus certaines. Il peut également simplement réessayer avec des paramètres mélangés et voir comment l'autre variante se classe dans son classement interne. Et il utilisera ce type de logique dans tout votre code. s'il remplace certains codes par du code syntaxiquement totalement inégal mais sémantiquement équivalent (l'ancien "xor foo, foo" est simplement l'optimisation la plus simple, bien que dépassée, de ce type). Si vous le demandez gentiment, vous pouvez omettre les normes de virgule flottante IEEE et activer encore plus d'optimisations comme la réorganisation des opérandes en virgule flottante. Une fois qu'il a massé et massacré votre code, il peut répéter tout le processus, car souvent, certaines optimisations jettent les bases d'optimisations encore plus certaines. Il peut également simplement réessayer avec des paramètres mélangés et voir comment l'autre variante se classe dans son classement interne. Et il utilisera ce type de logique dans tout votre code. Si vous le demandez gentiment, vous pouvez omettre les normes de virgule flottante IEEE et activer encore plus d'optimisations comme la réorganisation des opérandes en virgule flottante. Une fois qu'il a massé et massacré votre code, il peut répéter tout le processus, car souvent, certaines optimisations jettent les bases d'optimisations encore plus certaines. Il peut également simplement réessayer avec des paramètres mélangés et voir comment l'autre variante se classe dans son classement interne. Et il utilisera ce type de logique dans tout votre code. Si vous le demandez gentiment, vous pouvez omettre les normes de virgule flottante IEEE et activer encore plus d'optimisations comme la réorganisation des opérandes en virgule flottante. Une fois qu'il a massé et massacré votre code, il peut répéter tout le processus, car souvent, certaines optimisations jettent les bases d'optimisations encore plus certaines. Il peut également simplement réessayer avec des paramètres mélangés et voir comment l'autre variante se classe dans son classement interne. Et il utilisera ce type de logique dans tout votre code. Il peut également simplement réessayer avec des paramètres mélangés et voir comment l'autre variante se classe dans son classement interne. Et il utilisera ce type de logique dans tout votre code. Il peut également simplement réessayer avec des paramètres mélangés et voir comment l'autre variante se classe dans son classement interne. Et il utilisera ce type de logique dans tout votre code.
Donc, comme vous pouvez le voir, il y a de nombreuses raisons pour lesquelles certaines implémentations C ++ ou C seront plus rapides.
Cela dit, de nombreuses optimisations peuvent être faites en C ++ qui épateront tout ce que vous pourriez faire avec C #, en particulier dans le domaine du calcul des nombres, en temps réel et proche du métal, mais pas exclusivement là-bas. Vous n'avez même pas besoin de toucher un seul pointeur pour parcourir un long chemin.
Donc, selon ce que vous écrivez, j'irais avec l'un ou l'autre. Mais si vous écrivez quelque chose qui ne dépend pas du matériel (pilote, jeu vidéo, etc.), je ne m'inquiéterais pas des performances de C # (encore une fois, je ne peux pas parler de Java). Ça ira très bien.
<<<<<<<<<<
Généralement, certains arguments généralisés peuvent sembler cool dans des articles spécifiques, mais ne semblent généralement pas crédibles.
Bref, pour faire la paix: AOT est génial, tout comme JIT . La seule bonne réponse peut être: cela dépend. Et les vrais gens intelligents savent que vous pouvez de toute façon utiliser le meilleur des deux mondes.
la source
Cela ne se produirait que si l'interpréteur Java produit du code machine qui est en fait mieux optimisé que le code machine que votre compilateur génère pour le code C ++ que vous écrivez, au point où le code C ++ est plus lent que Java et le coût d'interprétation.
Cependant, les chances que cela se produise sont assez faibles - à moins que Java n'ait peut-être une bibliothèque très bien écrite et que vous ayez votre propre bibliothèque C ++ mal écrite.
la source
En fait, C # ne fonctionne pas vraiment dans une machine virtuelle comme le fait Java. IL est compilé en langage d'assemblage, qui est entièrement du code natif et s'exécute à la même vitesse que le code natif. Vous pouvez pré-JIT une application .NET qui supprime entièrement le coût JIT, puis vous exécutez du code entièrement natif.
Le ralentissement avec .NET ne viendra pas parce que le code .NET est plus lent, mais parce qu'il fait beaucoup plus en arrière-plan pour faire des choses comme la collecte des ordures, vérifier les références, stocker des cadres de pile complets, etc. Cela peut être assez puissant et utile lorsque la construction d'applications, mais a également un coût. Notez que vous pouvez également faire toutes ces choses dans un programme C ++ (une grande partie des fonctionnalités principales de .NET est en fait du code .NET que vous pouvez afficher dans ROTOR). Cependant, si vous écriviez à la main la même fonctionnalité, vous vous retrouveriez probablement avec un programme beaucoup plus lent car le runtime .NET a été optimisé et finement réglé.
Cela dit, l'un des points forts du code géré est qu'il peut être entièrement vérifiable, c'est-à-dire. vous pouvez vérifier que le code n'accédera jamais à la mémoire d'un autre processus ou n'effectuera aucune opération non conforme avant de l'exécuter. Microsoft a un prototype de recherche d'un système d'exploitation entièrement géré qui a montré de manière surprenante qu'un environnement géré à 100% peut en fait fonctionner beaucoup plus rapidement que n'importe quel système d'exploitation moderne en tirant parti de cette vérification pour désactiver les fonctionnalités de sécurité qui ne sont plus nécessaires aux programmes gérés. (nous parlons comme 10x dans certains cas). La radio SE a un grand épisode parlant de ce projet.
la source
Dans certains cas, le code managé peut en fait être plus rapide que le code natif. Par exemple, les algorithmes de récupération de place "mark-and-sweep" permettent à des environnements comme le JRE ou le CLR de libérer un grand nombre d'objets de courte durée (généralement) en une seule passe, où la plupart des objets de tas C / C ++ sont libérés un par un. une fois.
De wikipedia :
Cela dit, j'ai écrit beaucoup de C # et beaucoup de C ++, et j'ai exécuté beaucoup de benchmarks. D'après mon expérience, C ++ est beaucoup plus rapide que C #, de deux manières: (1) si vous prenez du code que vous avez écrit en C #, portez-le en C ++, le code natif a tendance à être plus rapide. Combien plus vite? Eh bien, cela varie beaucoup, mais il n'est pas rare de voir une amélioration de la vitesse de 100%. (2) Dans certains cas, le garbage collection peut ralentir massivement une application gérée. Le .NET CLR fait un travail terrible avec de gros tas (par exemple,> 2 Go), et peut finir par passer beaucoup de temps en GC - même dans des applications qui ont peu ou même pas d'objets de durée de vie intermédiaire.
Bien sûr, dans la plupart des cas que j'ai rencontrés, les langages gérés sont assez rapides, de loin, et le compromis entre la maintenance et le codage pour les performances supplémentaires du C ++ n'est tout simplement pas bon.
la source
Voici un benchmark intéressant http://zi.fi/shootout/
la source
En fait, la JVM HotSpot de Sun utilise une exécution en "mode mixte". Il interprète le bytecode de la méthode jusqu'à ce qu'il détermine (généralement via un compteur quelconque) qu'un bloc de code particulier (méthode, boucle, bloc try-catch, etc.) va être beaucoup exécuté, puis il le compile par JIT. Le temps requis pour compiler une méthode en JIT prend souvent plus de temps que si la méthode devait être interprétée s'il s'agissait d'une méthode rarement exécutée. Les performances sont généralement plus élevées pour le «mode mixte» car la JVM ne perd pas de temps à JITing du code qui est rarement, voire jamais, exécuté. C # et .NET ne font pas cela. .NET JIT tout ce qui, souvent, fait perdre du temps.
la source
Allez lire sur Dynamo de HP Labs , un interpréteur pour PA-8000 qui fonctionne sur PA-8000, et exécute souvent les programmes plus rapidement qu'ils ne le font en mode natif. Alors ça ne paraîtra pas du tout surprenant!
Ne le considérez pas comme une "étape intermédiaire" - l'exécution d'un programme implique déjà beaucoup d'autres étapes, dans n'importe quelle langue.
Cela revient souvent à:
les programmes ont des points chauds, donc même si vous exécutez plus lentement 95% du corps du code que vous devez exécuter, vous pouvez toujours être compétitif en termes de performances si vous êtes plus rapide à 5%
un HLL en sait plus sur votre intention qu'un LLL comme C / C ++, et peut donc générer du code plus optimisé (OCaml en a encore plus, et en pratique est souvent encore plus rapide)
un compilateur JIT a beaucoup d'informations qu'un compilateur statique n'a pas (comme, les données réelles que vous avez cette fois)
un compilateur JIT peut effectuer des optimisations au moment de l'exécution que les éditeurs de liens traditionnels ne sont pas vraiment autorisés à faire (comme la réorganisation des branches afin que le cas commun soit plat, ou les appels de bibliothèque en ligne)
Dans l'ensemble, C / C ++ sont des langages assez médiocres pour les performances: il y a relativement peu d'informations sur vos types de données, aucune information sur vos données et aucun runtime dynamique pour permettre une optimisation au moment de l'exécution.
la source
Vous pouvez obtenir de courtes rafales lorsque Java ou CLR est plus rapide que C ++, mais dans l'ensemble, les performances sont pires pour la durée de vie de l'application: voir www.codeproject.com/KB/dotnet/RuntimePerformance.aspx pour obtenir des résultats à ce sujet.
la source
Voici la réponse de Cliff Click: http://www.azulsystems.com/blog/cliff/2009-09-06-java-vs-c-performanceagain
la source
C'est illogique. L'utilisation d'une représentation intermédiaire ne dégrade pas intrinsèquement les performances. Par exemple, llvm-gcc compile C et C ++ via LLVM IR (qui est une machine virtuelle à registre infini) en code natif et obtient d'excellentes performances (battant souvent GCC).
Voici quelques exemples:
Les machines virtuelles avec la compilation JIT facilitent la génération de code d'exécution (par exemple
System.Reflection.Emit
sur .NET) afin que vous puissiez compiler le code généré à la volée dans des langages comme C # et F # mais devez recourir à l'écriture d'un interpréteur relativement lent en C ou C ++. Par exemple, pour implémenter des expressions régulières.Certaines parties de la machine virtuelle (par exemple la barrière d'écriture et l'allocateur) sont souvent écrites dans l'assembleur codé à la main car C et C ++ ne génèrent pas de code assez rapidement. Si un programme met l'accent sur ces parties d'un système, il pourrait alors surpasser tout ce qui peut être écrit en C ou C ++.
La liaison dynamique du code natif nécessite la conformité à une ABI qui peut entraver les performances et empêcher l'optimisation de l'ensemble du programme, tandis que la liaison est généralement différée sur les machines virtuelles et peut bénéficier d'optimisations de l'ensemble du programme (comme les génériques réifiés de .NET).
J'aimerais également aborder certains problèmes liés à la réponse hautement votée de paercebal ci-dessus (parce que quelqu'un n'arrête pas de supprimer mes commentaires sur sa réponse) qui présente une vue polarisée contre-productive:
Par conséquent, la métaprogrammation de modèles ne fonctionne que si le programme est disponible au moment de la compilation, ce qui n'est souvent pas le cas, par exemple, il est impossible d'écrire une bibliothèque d'expressions régulières performante en vanilla C ++ car elle est incapable de générer du code à l'exécution (un aspect important de métaprogrammation).
En C #, cela n'est vrai que pour les types référence et n'est pas vrai pour les types valeur.
Les gens ont observé Java battre C ++ au test SOR du benchmark SciMark2 précisément parce que les pointeurs empêchent les optimisations liées aux alias.
Il convient également de noter que .NET spécialise les types de génériques dans les bibliothèques liées dynamiquement après la liaison, alors que C ++ ne le peut pas car les modèles doivent être résolus avant la liaison. Et évidemment, le gros avantage des génériques par rapport aux modèles est la compréhension des messages d'erreur.
la source
En plus de ce que certains ont dit, d'après ma compréhension, .NET et Java sont meilleurs pour l'allocation de mémoire. Par exemple, ils peuvent compacter la mémoire lorsqu'elle est fragmentée, tandis que C ++ ne le peut pas (en mode natif, mais c'est possible si vous utilisez un garbage collector intelligent).
la source
Pour tout ce qui nécessite beaucoup de vitesse, la JVM appelle simplement une implémentation C ++, il s'agit donc davantage de la qualité de leurs bibliothèques que de la qualité de la JVM pour la plupart des choses liées au système d'exploitation. Le ramassage des ordures réduit votre mémoire de moitié, mais l'utilisation de certaines des fonctionnalités STL et Boost plus sophistiquées aura le même effet mais avec plusieurs fois le potentiel de bogue.
Si vous utilisez uniquement des bibliothèques C ++ et de nombreuses fonctionnalités de haut niveau dans un grand projet avec de nombreuses classes, vous vous retrouverez probablement plus lentement que d'utiliser une JVM. Sauf beaucoup plus sujet aux erreurs.
Cependant, l'avantage du C ++ est qu'il vous permet de vous optimiser, sinon vous êtes coincé avec ce que fait le compilateur / jvm. Si vous créez vos propres conteneurs, écrivez votre propre gestion de la mémoire alignée, utilisez SIMD et passez à l'assemblage ici et là, vous pouvez accélérer au moins 2x à 4x par rapport à ce que la plupart des compilateurs C ++ feront seuls. Pour certaines opérations, 16x-32x. Cela utilise les mêmes algorithmes, si vous utilisez de meilleurs algorithmes et parallélisez, les augmentations peuvent être dramatiques, parfois des milliers de fois plus rapides que les méthodes couramment utilisées.
la source
Je le regarde à partir de quelques points différents.
la source
Une réponse très courte: avec un budget fixe, vous obtiendrez une application Java plus performante qu'une application C ++ (considérations ROI) De plus, la plate-forme Java a des profileurs plus décents, qui vous aideront à identifier vos hotspots plus rapidement
la source