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...
Edit 2011-06-10: En C ++, jouer avec les types se fait au moment de la compilation, c'est-à-dire produire du code générique qui appelle du code non générique (par exemple un analyseur générique de la chaîne au type T, appelant l'API de bibliothèque standard pour les types T qu'il reconnaît, et rendre l'analyseur facilement extensible par son utilisateur) est très simple et très efficace, alors que l'équivalent en Java ou C # est au mieux pénible à écrire, et sera toujours plus lent et résolu à l'exécution même lorsque les types sont connus au moment de la compilation, ce qui signifie que votre seul espoir est que le JIT intègre le tout.
...
Edit 2011-09-20: L'équipe derrière Blitz ++ ( page d'accueil , Wikipédia ) est allée dans ce sens, et apparemment, leur objectif est d'atteindre les performances de FORTRAN sur les calculs scientifiques en passant autant que possible de l'exécution à la compilation, via la métaprogrammation de modèles C ++ . Ainsi , le « Je n'ai pas encore si voir un effet réel devie sur cette » partie I écrit ci - dessus semble - t exist dans la vie réelle.
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 de finally
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.
Edit 2011-10-04: Pour des exemples d'allocateurs efficaces: Sur les plates-formes Windows, depuis Vista, le tas de faible fragmentation est activé par défaut. Pour les versions précédentes, le LFH peut être activé en appelant la fonction WinAPI HeapSetInformation ). Sur d'autres systèmes d'exploitation, des allocateurs alternatifs sont fournis (voirhttps://secure.wikimedia.org/wikipedia/en/wiki/Malloc pour une liste)
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:
- Les génériques ne sont pas aussi puissants que les templates ( essayez d'écrire une méthode Parse générique efficace (de chaîne à T), ou un équivalent efficace de boost :: lexical_cast en C # pour comprendre le problème )
- RAII reste inégalé ( GC peut toujours fuir (oui, j'ai dû gérer ce problème) et ne traitera que la mémoire. Même C #
using
n'est pas aussi facile et puissant car écrire une implémentation Dispose correcte est difficile )
- C #
readonly
et Java ne final
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)
«Nous constatons qu'en ce qui concerne les performances, C ++ l'emporte largement. Cependant, il a également nécessité les efforts de réglage les plus étendus, dont beaucoup ont été réalisés à un niveau de sophistication qui ne serait pas disponible pour le programmeur moyen.
[...] La version Java était probablement la plus simple à implémenter, mais la plus difficile à analyser pour les performances. Plus précisément, les effets liés au ramassage des ordures étaient compliqués et très difficiles à régler. "
Sources:
Modifier (20/09/2011)
«Le mot courant chez Facebook est que« le code C ++ raisonnablement écrit s'exécute rapidement », ce qui souligne l'énorme effort consacré à l'optimisation du code PHP et Java. Paradoxalement, le code C ++ est plus difficile à écrire que dans d'autres langages, mais un code efficace est un beaucoup plus facile [d'écrire en C ++ que dans d'autres langages]. "
- Herb Sutter à // build / , citant Andrei Alexandrescu
Sources: