J'ai remarqué que certaines applications ou algorithmes construits sur un langage de programmation, par exemple C ++ / Rust, s'exécutent plus rapidement ou plus facilement que ceux basés sur Java / Node.js, exécutés sur le même ordinateur. J'ai quelques questions à ce sujet:
- Pourquoi cela arrive-t-il?
- Qu'est-ce qui gouverne la "vitesse" d'un langage de programmation?
- Cela at-il quelque chose à voir avec la gestion de la mémoire?
J'apprécierais vraiment si quelqu'un m'écroulait ça.
programming-languages
compilers
evil_potato
la source
la source
Réponses:
Lors de la conception et de la mise en œuvre d'un langage de programmation, de nombreux choix peuvent affecter les performances. Je n'en mentionnerai que quelques-uns.
Chaque langue doit finalement être exécutée en exécutant du code machine. Un langage "compilé" tel que C ++ est analysé, décodé et traduit en code machine une seule fois, au moment de la compilation. Un langage "interprété", s'il est implémenté directement, est décodé à l'exécution, à chaque étape, à chaque fois. Autrement dit, chaque fois que nous exécutons une instruction, l’interprète doit vérifier s’il s’agit d’un if-then-else, d’une cession, etc., et agir en conséquence. Cela signifie que si nous bouclons 100 fois, nous décodons le même code 100 fois, ce qui nous fait perdre du temps. Heureusement, les interprètes optimisent souvent cela, par exemple grâce à un système de compilation juste à temps. (Plus exactement, il n’existe pas de langage "compilé" ou "interprété" - c’est une propriété de l’implémentation, pas du langage.
Différents compilateurs / interprètes effectuent différentes optimisations.
Si la langue dispose d'une gestion automatique de la mémoire, son implémentation doit effectuer une récupération de place. Cela a un coût d'exécution, mais soulage le programmeur d'une tâche sujette aux erreurs.
Une langue peut être plus proche de la machine, ce qui permet au programmeur expert de tout optimiser et d'optimiser les performances du processeur. Cependant, on peut se demander si cela est réellement bénéfique dans la pratique, car la plupart des programmeurs n'optimisent pas vraiment la micro-optimisation, et souvent un langage de niveau supérieur peut être optimisé par le compilateur mieux que ce que ferait un programmeur moyen. (Cependant, parfois, être plus éloigné de la machine peut aussi avoir ses avantages! Par exemple, Haskell est d'un niveau extrêmement élevé, mais grâce à ses choix de conception, il est capable de comporter des fils verts très légers.)
La vérification de type statique peut également aider à l'optimisation. Dans un langage interprété et typé dynamiquement, chaque fois que l’on calcule
x - y
, l’interprète doit souvent vérifier si les deuxx,y
sont des nombres et, par exemple, déclencher une exception. Cette vérification peut être ignorée si les types ont déjà été vérifiés lors de la compilation.Certaines langues signalent toujours les erreurs d'exécution de manière saine. Si vous écrivez
a[100]
en Javaa
avec seulement 20 éléments, vous obtenez une exception d'exécution. En C, cela provoquerait un comportement indéfini, ce qui signifie que le programme pourrait se bloquer, écraser des données aléatoires en mémoire ou même exécuter absolument n'importe quoi d' autre (la norme ISO C ne pose aucune limite). Cela nécessite une vérification à l'exécution, mais fournit une sémantique bien plus agréable au programmeur.Cependant, gardez à l'esprit que, lors de l'évaluation d'une langue, les performances ne sont pas tout. Ne soyez pas obsédé par ça. Il est courant d’essayer de tout optimiser au maximum, sans pour autant savoir qu’un algorithme / structure de données inefficace est utilisé. Knuth a dit une fois "l'optimisation prématurée est la racine de tout le mal".
Ne sous-estimez pas à quel point il est difficile d'écrire un programme correctement . Souvent, il peut être préférable de choisir une langue "plus lente" qui possède une sémantique plus conviviale. De plus, s’il existe des composants critiques en termes de performances, ceux-ci peuvent toujours être implémentés dans un autre langage. À titre de référence, lors du concours de programmation ICFP 2016 , voici les langues utilisées par les gagnants:
Aucun d'entre eux n'utilisait une seule langue.
la source
La "rapidité" d'un langage de programmation n'existe pas. Il n'y a que la vitesse d'un programme particulier écrit par un programmeur particulier exécuté par une version particulière d'une implémentation particulière d'un moteur d'exécution particulier s'exécutant dans un environnement particulier.
L'exécution du même code écrit dans le même langage sur le même ordinateur avec des implémentations différentes peut entraîner d'énormes différences de performances. Ou même en utilisant différentes versions de la même implémentation. Par exemple, si vous exécutez exactement le même benchmark ECMAScript sur la même machine avec une version de SpiderMonkey d'il y a 10 ans, une version de cette année entraînera probablement une augmentation des performances comprise entre 2 × –5 ×, voire même 10 ×. Cela signifie-t-il que ECMAScript est 2 × plus rapide que ECMAScript, car exécuter le même programme sur la même machine est 2 × plus rapide avec la nouvelle implémentation? Cela n'a pas de sens.
Pas vraiment.
Ressources. Argent. Microsoft emploie probablement plus de personnes qui préparent du café pour leurs programmeurs de compilateur que l'ensemble de la communauté PHP, Ruby et Python réunies, qui emploie des personnes sur leurs ordinateurs virtuels.
Pour plus ou moins toute fonctionnalité d’un langage de programmation ayant un impact sur les performances, il existe également une solution. Par exemple, C (je l'utilise ici en tant que remplaçant d'une classe de langages similaires, dont certains existaient même avant le C) n'est pas sûr pour la mémoire, de sorte que plusieurs programmes en C exécutés en même temps peuvent piétiner la mémoire de l'autre. Donc, nous inventons la mémoire virtuelle et faisons en sorte que tous les programmes C passent par une couche d'indirection afin qu'ils puissent prétendre qu'ils sont les seuls fonctionnant sur la machine. Cependant, c'est lent et nous avons donc inventé la MMU et implémenté la mémoire virtuelle dans le matériel pour l'accélérer.
Mais! Les langues sans danger pour la mémoire n'ont pas besoin de tout ça! Avoir de la mémoire virtuelle ne les aide pas un bit. En réalité, c'est pire: non seulement la mémoire virtuelle n'aide pas les langages sécurisés, mais la mémoire virtuelle, même lorsqu'elle est mise en œuvre matériellement, a toujours un impact sur les performances. Cela peut être particulièrement préjudiciable aux performances des éboueurs (c’est ce qu’utilisent un grand nombre d’implémentations de langages sécurisés pour la mémoire).
Autre exemple: les processeurs grand public modernes utilisent des astuces sophistiquées pour réduire la fréquence d’absence de mémoire cache. Beaucoup de ces astuces consistent à essayer de prédire quel code sera exécuté et quelle mémoire sera nécessaire dans le futur. Cependant, pour les langues avec un degré élevé de polymorphisme d'exécution (par exemple, les langages OO), il est vraiment très difficile de prédire ces modèles d'accès.
Mais il existe un autre moyen: le coût total des échecs de mémoire cache correspond au nombre d’erreurs de cache multiplié par le coût d’un échec individuel. Les processeurs traditionnels tentent de réduire le nombre de manquements, mais que se passerait-il si vous pouviez réduire le coût d'un manquement individuel?
Le processeur Azul Vega-3 a été spécialement conçu pour exécuter des machines virtuelles virtualisées. Il disposait d'une MMU très puissante avec des instructions spécifiques pour aider à la récupération de place et à la détection des échappements (l'équivalent dynamique de l'analyse des échappements statiques), ainsi que de puissants contrôleurs de mémoire et l'ensemble du système. pourrait encore progresser avec plus de 20000 casses en suspens en vol. Malheureusement, comme la plupart des processeurs spécifiques à une langue, sa conception a tout simplement été dépassée et forcée par les «géants» Intel, AMD, IBM et autres.
L'architecture de la CPU n'est qu'un exemple qui a une incidence sur la facilité ou la difficulté d'obtenir une implémentation hautes performances d'un langage. Un langage comme C, C ++, D, Rust qui conviendra parfaitement au modèle de programmation CPU moderne, sera plus facile à créer rapidement qu’un langage qui doit "combattre" et contourner le CPU, comme Java, ECMAScript, Python, Ruby , PHP.
Vraiment, tout est une question d'argent. Si vous dépensez des sommes égales pour développer un algorithme hautes performances dans ECMAScript, une implémentation hautes performances d’ECMAScript, un système d’exploitation hautes performances conçu pour ECMAScript, une unité centrale hautes performances conçue pour ECMAScript comme précédemment des décennies pour faire en sorte que les langues de type C aillent vite, vous obtiendrez probablement des performances égales. À l'heure actuelle, beaucoup plus d'argent a été dépensé pour créer rapidement des langages de type C plutôt que pour des langages de type ECMAScript, et les suppositions de ces langages sont intégrées dans toute la pile, des MMU aux processeurs, en passant par les systèmes d'exploitation. systèmes de mémoire virtuelle jusqu'aux bibliothèques et les cadres.
Personnellement, je connais très bien Ruby (qui est généralement considéré comme un "langage lent"), je vais donc donner deux exemples: la
Hash
classe (l'une des structures de données centrales dans Ruby, un dictionnaire clé-valeur) dans Rubinius L’implémentation de Ruby est écrite en Ruby pur à 100% et a à peu près les mêmes performances que leHash
classe dans YARV (l'implémentation la plus largement utilisée), écrite en C. Et il existe une bibliothèque de manipulation d'images écrite en tant qu'extension C pour YARV, qui possède également une "version de secours" (lente) de Ruby pure pour les implémentations ne supporte pas C qui utilise une tonne de trucs Ruby hautement dynamiques et réfléchissants; Une branche expérimentale de JRuby, utilisant le framework d’interpréteur Truffle AST et le framework de compilation Graal JIT d’Oracle Labs, peut exécuter cette "version de secours" pure Ruby aussi rapidement que YARV peut exécuter la version C hautement optimisée d’origine. Ceci est tout simplement (enfin, n'importe quoi) réalisé par des personnes très intelligentes qui effectuent des tâches très intelligentes avec des optimisations d’exécution dynamiques, une compilation JIT et une évaluation partielle.la source
int
pour des raisons de performances, malgré le fait que les entiers non bornés tels que ceux utilisés par Python sont beaucoup plus naturels sur le plan mathématique. L'implémentation d'entiers non liés dans le matériel ne serait pas aussi rapide que des entiers de taille fixe. Les langages qui tentent de masquer les détails d'implémentation nécessitent des optimisations complexes pour s'approcher des implémentations C naïves.Théoriquement, si vous écrivez du code qui fait exactement la même chose dans deux langages et que vous compilez les deux à l'aide d'un compilateur "parfait", les performances des deux devraient être identiques.
En pratique, la performance sera différente pour plusieurs raisons:
Certaines langues sont plus difficiles à optimiser.
Cela inclut notamment les fonctionnalités qui rendent le code plus dynamique (typage dynamique, méthodes virtuelles, pointeurs de fonction), mais aussi, par exemple, le garbage collection.
Il existe différentes manières de créer du code en utilisant de telles fonctionnalités rapidement, mais cela finit toujours par être au moins un peu plus lent que de ne pas les utiliser.
Certaines implémentations de langage doivent compiler au moment de l'exécution.
Cela s'applique en particulier aux langages avec des machines virtuelles (comme Java) et aux langages qui exécutent le code source, sans étape intermédiaire pour le binaire (comme JavaScript).
De telles implémentations de langage doivent faire plus de travail au moment de l'exécution, ce qui affecte les performances, en particulier le temps / les performances de démarrage, peu après le démarrage.
Les implémentations de langage consacrent intentionnellement moins de temps aux optimisations qu’elles ne le pourraient.
Tous les compilateurs se soucient des performances du compilateur lui-même, pas seulement du code généré. Ceci est particulièrement important pour les compilateurs d'exécution (compilateurs JIT), où la compilation prend trop de temps ralentit l'exécution de l'application. Mais cela s’applique aussi aux compilateurs à l’avant-garde, comme ceux du C ++. Par exemple, l’ allocation de registres est un problème NP-complet. Il n’est donc pas réaliste de le résoudre parfaitement. Nous utilisons plutôt des méthodes heuristiques exécutables dans un délai raisonnable.
Différences d'idiomes dans différentes langues.
Un code écrit dans le style commun à un certain langage (code idiomatique) à l'aide de bibliothèques communes peut entraîner une différence de performances, car un tel code idiomatique se comporte de manière très différente dans chaque langage.
A titre d'exemple, considérons
vector[i]
en C ++,list[i]
en C # etlist.get(i)
en Java. Le code C ++ ne vérifie probablement pas de plage et n'effectue aucun appel virtuel, le code C # effectue une vérification de plage, mais aucun appel virtuel et le code Java effectue une vérification de plage et il s'agit d'un appel virtuel. Les trois langues prennent en charge les méthodes virtuelles et non virtuelles et C ++ et C # peuvent inclure la vérification de plage ou l'éviter lors de l'accès au tableau sous-jacent. Mais les bibliothèques standard de ces langages ont décidé d'écrire ces fonctions équivalentes différemment et, par conséquent, leurs performances seront différentes.Certains compilateurs peuvent ne pas avoir certaines optimisations.
Les rédacteurs de compilateur ont des ressources finies, ils ne peuvent donc pas implémenter toutes les optimisations possibles, même en ignorant les autres problèmes. Et les ressources dont ils disposent pourraient être concentrés sur un domaine d'optimisation plus que sur d'autres. En conséquence, les codes écrits dans différentes langues peuvent avoir des performances différentes en raison des différences entre leurs compilateurs, même s'il n'existe aucune raison fondamentale pour laquelle une langue devrait être plus rapide ou même plus facile à optimiser que l'autre.
la source
C'est une question très générale, mais dans votre cas, la réponse pourrait être simple. C ++ est compilé en code machine, où Java est compilé en code octet Java, qui est ensuite exécuté sur une machine virtuelle Java (bien qu'il existe également une compilation juste à temps, qui compile davantage le code octet Java en code machine natif). Une autre différence pourrait être garbage collection, un service que seul Java fournit.
la source
Votre question est trop générale, je crains donc de ne pas pouvoir vous donner une réponse exacte dont vous avez besoin.
Pour ma meilleure explication, examinons les plateformes C ++ et .Net.
C ++, très proche du code machine et l’un des programmes favoris du passé (comme il y a plus de 10 ans?) En raison de ses performances. Il n’ya pas beaucoup de ressources nécessaires pour développer et exécuter un programme C ++, même avec l’IDE, ils sont considérés comme très légers et très rapides. Vous pouvez également écrire du code C ++ dans la console et développer un jeu à partir de celle-ci. En termes de mémoire et de ressources, j’ai oublié à peu près la capacité d’un ordinateur, mais sa capacité est inégalée par rapport à la génération actuelle de langage de programmation.
Maintenant, regardons. Net. La condition préalable au développement .Net est un IDE géant qui ne consiste pas en un seul type de langages de programmation. Même si vous souhaitez simplement développer en C #, l'EDI lui-même inclut par défaut de nombreuses plates-formes de programmation telles que J #, VB, mobile, etc. Sauf si vous effectuez une installation personnalisée et installez uniquement ce que vous voulez, à condition que vous ayez suffisamment d'expérience pour jouer avec l'installation de l'EDI.
Outre l'installation du logiciel IDE lui-même, .Net est doté d'une énorme capacité de bibliothèques et de frameworks afin de faciliter l'accès à tout type de plate-forme dont les développeurs ont besoin ET dont ils n'ont pas besoin.
Développer en .Net peut être une expérience amusante car de nombreux composants et fonctions courants sont disponibles par défaut. Vous pouvez glisser-déposer et utiliser de nombreuses méthodes de validation, lecture de fichiers, accès à une base de données et bien plus encore dans l'EDI.
En conséquence, c'est un compromis entre les ressources informatiques et la vitesse de développement. Ces bibliothèques et framework utilisent beaucoup de mémoire et de ressources. Lorsque vous exécutez un programme dans .NET IDE, cela peut prendre beaucoup de temps pour essayer de compiler les bibliothèques, les composants et tous vos fichiers. Et lorsque vous effectuez un débogage, votre ordinateur a besoin de beaucoup de ressources pour gérer le processus de débogage dans votre IDE.
Habituellement, pour faire du développement .Net, vous avez besoin d’un bon ordinateur avec un peu de mémoire vive et un processeur. Autrement, vous pourriez aussi bien ne pas programmer du tout. À cet égard, la plate-forme C ++ est bien meilleure que .Net. Bien que vous ayez encore besoin d’un bon ordinateur, la capacité n’est pas un problème, comparez-le à .Net.
J'espère que ma réponse pourrait aider avec votre question. Faites-moi savoir au cas où vous voudriez en savoir plus.
MODIFIER:
Juste une précision sur mon point principal, je réponds principalement à la question "Qu'est-ce qui régit la vitesse de programmation".
Du point de vue de l'EDI, l'utilisation du langage C ++ ou .Net dans l'EDI relatif n'a pas d'incidence sur la vitesse d'écriture du code, mais sur le développement, car l'exécution du programme par le compilateur Visual Studio est plus longue, mais l'EDI C ++ est beaucoup plus légère. et consommer moins de ressources informatiques. Donc, à long terme, vous pouvez programmer plus rapidement avec le type d'EDI C ++, comparé à l'IDE .Net, qui dépend fortement des bibliothèques et du framework. Si vous prenez le temps d'attente de l'EDI pour démarrer, compiler, exécuter le programme, etc., cela affectera la vitesse de programmation. À moins que "la vitesse de programmation" ne soit en réalité centré que sur "la vitesse d'écriture du code".
La quantité de bibliothèques et d'infrastructure dans Visual Studio consomme également la capacité de l'ordinateur. Je ne sais pas si cela répond à la question de "gestion de la mémoire", mais je tiens à souligner que Visual Studio IDE peut utiliser beaucoup de ressources lors de son exécution et ralentir ainsi la "vitesse de programmation" globale. Bien entendu, cela n’a rien à voir avec la "rapidité de l’écriture de code".
Comme vous l'avez peut-être deviné, je ne connais pas très bien le C ++, je l'utilise seulement à titre d'exemple. Mon point principal concerne le type d'IDE lourd de Visual Studio qui consomme des ressources informatiques.
Si j'ai eu l'idée et que je n'ai pas du tout répondu à la question du démarreur de fil, veuillez vous excuser pour le long post. Mais je conseillerais au débutant de préciser la question et de demander exactement ce qu'il / elle doit savoir sur "plus vite" et "plus lentement"
la source