Comment un langage dont le compilateur est écrit en C peut-il être plus rapide que C?

175

En regardant la page Web de Julia , vous pouvez voir quelques points de repère de plusieurs langues à travers plusieurs algorithmes (timings montrés ci-dessous). Comment un langage avec un compilateur écrit à l'origine en C peut-il surpasser le code C?

entrez la description de l'image ici Figure: temps de référence par rapport à C (plus petit est bon, meilleur est C = 1,0).

Programmeur en difficulté
la source
382
Comment une voiture, qui est un objet fabriqué par l'homme, peut-elle se déplacer plus vite qu'un homme?
babou
19
Selon le tableau, Python est plus lent que le C. Pensez-vous qu'il est impossible d'écrire un compilateur C en Python qui génère le même code que votre compilateur C préféré? Et dans quelle langue est-il écrit?
Carsten S
6
Le commentaire de babou était net, mais je ne pense pas que nous ayons besoin de plusieurs versions du même.
Raphaël
14
Une pensée liée. De nombreux compilateurs sont auto-hébergés , ce qui signifie qu'ils sont écrits dans leur propre langue (souvent plus un assemblage) et compilés avec la version précédente de lui-même. Pourtant, les compilateurs deviennent meilleurs et plus rapides. Coup d'esprit .
Schwern

Réponses:

263

Il n'y a pas de relation nécessaire entre l'implémentation du compilateur et la sortie du compilateur. Vous pouvez écrire un compilateur dans un langage tel que Python ou Ruby, dont les implémentations les plus courantes sont très lentes, et ce compilateur pourrait générer un code machine hautement optimisé capable de surperformer C. Le compilateur lui-même prendrait beaucoup de temps à s'exécuter, car sonle code est écrit dans une langue lente. (Pour être plus précis, écrit dans un langage avec une implémentation lente. Les langages ne sont pas vraiment foncièrement rapides ou lents, comme le souligne Raphael dans un commentaire. Je développe cette idée ci-dessous.) Le programme compilé serait aussi rapide que son propre implémentation autorisée - nous pourrions écrire un compilateur en Python qui génère le même code machine qu'un compilateur Fortran, et nos programmes compilés seraient aussi rapides que Fortran, même s’ils prendraient beaucoup de temps à compiler.

C'est une autre histoire si nous parlons d'un interprète. Les interprètes doivent être en cours d'exécution pendant l'exécution du programme qu'ils interprètent. Il existe donc un lien entre le langage dans lequel l'interpréteur est implémenté et les performances du code interprété. Un langage interprété fonctionnant plus rapidement que le langage dans lequel l'interpréteur est implémenté nécessite une optimisation d'exécution astucieuse, et les performances finales peuvent dépendre de la mesure dans laquelle un élément de code est susceptible de ce type d'optimisation. De nombreux langages, tels que Java et C #, utilisent des exécutions avec un modèle hybride qui combine certains des avantages des interprètes avec ceux des compilateurs.

À titre d'exemple concret, examinons de plus près Python. Python a plusieurs implémentations. Le plus courant est CPython, un interpréteur de bytecode écrit en C. Il existe également PyPy, qui est écrit dans un dialecte spécialisé de Python appelé RPython, qui utilise un modèle de compilation hybride un peu comme la machine virtuelle Java. PyPy est beaucoup plus rapide que CPython dans la plupart des tests. il utilise toutes sortes de trucs incroyables pour optimiser le code au moment de l'exécution. Cependant, le langage Python utilisé par PyPy est exactement le même que celui utilisé par CPython, à l’exception de quelques différences qui n’affectent pas les performances.

Supposons que nous ayons écrit un compilateur en langage Python pour Fortran. Notre compilateur produit le même code machine que GFortran. Maintenant, nous compilons un programme Fortran. Nous pouvons exécuter notre compilateur sur CPython ou sur PyPy, car il est écrit en Python et que ces deux implémentations utilisent le même langage Python. Nous trouverons que si nous utilisons notre compilateur sur CPython, puis sur PyPy, puis compilons le même source Fortran avec GFortran, nous obtiendrons exactement le même code machine à trois reprises, de sorte que le programme compilé sera toujours exécuté. à peu près à la même vitesse. Cependant, le temps nécessaire pour produire ce programme compilé sera différent. CPython prendra probablement plus de temps que PyPy, et PyPy prendra plus de temps que GFortran, même si tous produiront le même code machine à la fin.

En parcourant le tableau des points de repère du site Web de Julia, il semble qu'aucune des langues exécutées sur les interprètes (Python, R, Matlab / Octave, Javascript) n'ait de repère où ils battent C. C'est généralement conforme à ce que j'attendais de voir, Bien que je puisse imaginer du code écrit avec la bibliothèque hautement optimisée Numpy de Python (écrite en C et Fortran) battant certaines implémentations C possibles d’un code similaire. Les langages égaux ou supérieurs à C sont en cours de compilation (Fortran, Julia ) ou utilisent un modèle hybride avec compilation partielle (Java et probablement LuaJIT). PyPy utilise également un modèle hybride. Il est donc tout à fait possible que si nous utilisions le même code Python sur PyPy au lieu de CPython, nous le verrions en réalité battre le C sur certains points de repère.

tsleyson
la source
9
C'est une réponse incroyable. Très clair, compréhensible et informatif. Merci beaucoup d'avoir pris le temps de l'écrire!
Alex A.
7
Le javascript et le java sont exécutés avec un compilateur JIT, mais java a un test où il est plus rapide que le c. Les compilateurs C / C ++ peuvent optimiser le code (généralement) beaucoup plus que quelqu'un écrivant manuellement un assemblage, tout simplement parce que le compilateur dispose de plus d'informations. Bien sûr, en théorie, la personne pourrait écrire un meilleur code d'assemblage, mais cela nécessite davantage de connaissances et de compétences que la plupart des gens. Les langues JIT peuvent être développées encore plus, en étant en mesure d'optimiser le fonctionnement exact de la machine
Programmdude
Les optimisations du compilateur sont une chose importante à considérer. Un compilateur très intelligent reconnaîtrait que le programme est une référence synthétique et optimiserait simplement à peu près tout le code, en créant simplement la sortie attendue.
ghellquist
@ghellquist Bien sûr, si le benchmark est suffisamment artificiel et que le compilateur est suffisamment intelligent. Cela ne concerne pas directement ou directement le langage d'implémentation du compilateur, donc je ne l'ai pas mentionné ici.
Tsleyson
97

Comment une machine construite par un homme peut-elle être plus forte qu'un homme? C'est exactement la même question.

La réponse est que la sortie du compilateur dépend des algorithmes mis en œuvre par ce compilateur, et non de la langue utilisée pour l'implémenter. Vous pourriez écrire un compilateur très lent et inefficace qui produit un code très efficace. Un compilateur n'a rien de spécial: il s'agit simplement d'un programme qui prend des entrées et produit des sorties.

David Richerby
la source
33
Comment un programme d'échecs peut-il battre l'humain qui l'a écrit?
Thorbjørn Ravn Andersen
25
En faisant de meilleurs mouvements! <rimshot>
Tony Ennis
Pour paraphraser la réponse de Penn Gilette, pourquoi un ordinateur ne peut-il pas battre un homme aux échecs?
Dave Kanter
90

Je veux faire un point contre une hypothèse commune qui, à mon avis, est fallacieuse au point d’être préjudiciable au moment de choisir les outils d’un travail.

Il n’existe pas de langage lent ou rapide. ¹

En route vers la CPU, il y a plusieurs étapes ².

  1. Au moins un programmeur avec certaines compétences.
  2. Le langage (formel) dans lequel ils programment ("code source").
  3. Les bibliothèques qu'ils utilisent.
  4. Quelque chose qui traduit le code source en code machine (compilateurs, interprètes).
  5. L'architecture matérielle globale, par exemple le nombre d'unités de traitement et la structure de la hiérarchie de la mémoire.
  6. Le système d'exploitation qui gère le matériel.
  7. Optimisations sur le processeur.

Chaque élément contribue à la durée d'exécution réelle que vous pouvez mesurer, parfois de manière importante. Différentes "langues" se concentrent sur différentes choses³.

Juste pour donner quelques exemples.

  • 1 vs 2-4 : un programmeur C moyen produira probablement un code bien pire qu'un programmeur Java moyen, à la fois en termes de correction et d'efficacité. C'est parce que le programmeur a plus de responsabilités en C.

  • 1/4 vs 7 : dans un langage de bas niveau comme le C, vous pourrez peut-être exploiter certaines fonctionnalités de la CPU en tant que programmeur . Dans les langages de niveau supérieur, seul le compilateur / interprète peut le faire, et uniquement s'il connaît le processeur cible.

  • 1/4 vs 5 : voulez-vous ou devez-vous contrôler la disposition de la mémoire afin de mieux utiliser l'architecture de la mémoire? Certaines langues vous permettent de contrôler cela, d'autres non.

  • 2/4 vs 3 : Python interprété lui-même est horriblement lent, mais il existe des liaisons populaires pour des bibliothèques hautement optimisées, compilées de manière native pour le calcul scientifique. Donc, faire certaines choses en Python est rapide au final, si l'essentiel du travail est fait par ces bibliothèques.

  • 2 vs 4 : l'interpréteur Ruby standard est assez lent. JRuby, en revanche, peut être très rapide. C'est la même langue est rapide en utilisant un autre compilateur / interprète.

  • 1/2 vs 4 : En utilisant les optimisations du compilateur, un code simple peut être traduit en code machine très efficace.

En fin de compte, le critère que vous avez trouvé n’a pas beaucoup de sens, du moins pas lorsque vous le présentez. Même si tout ce qui vous intéresse, c'est le temps d'exécution, vous devez spécifier la chaîne entière du programmeur au processeur; échanger n'importe lequel des éléments peut changer radicalement les résultats.

Pour être clair, cela répond à la question parce que cela montre que le langage dans lequel le compilateur (étape 4) est écrit n’est qu’un élément du puzzle, et n’est probablement pas pertinent du tout (voir autres réponses).


  1. Il existe certainement des fonctionnalités linguistiques plus coûteuses à mettre en œuvre que d’autres. Mais l'existence de fonctionnalités ne signifie pas que vous devez les utiliser, et une fonctionnalité coûteuse peut permettre d'éviter l'utilisation de nombreuses fonctionnalités moins chères et donc de payer en fin de compte. (De d'autres avantages ne sont pas mesurables dans le temps.)
  2. Je saute le niveau algorithmique car il ne s'applique pas toujours et est principalement indépendant du langage de programmation utilisé. Gardez à l'esprit que différents algorithmes se prêtent mieux à différents matériels, par exemple.
  3. Je n'entre délibérément pas dans des mesures de réussite différentes: efficacité du temps d'exécution, efficacité de la mémoire, temps du développeur, sécurité, sécurité (justifiable (prouvable?), Support des outils, indépendance de la plate-forme, ...

    La comparaison de langues avec une métrique, même si elles ont été conçues pour des objectifs complètement différents, est une grave erreur.

Raphaël
la source
1
@ Babou D'accord, très bonne explication. Alors, quelle serait une meilleure métrique, ou peut-être un ensemble de métriques , pouvant être utilisé pour comparer des langages avec leurs compilateurs / interprètes respectifs? En outre, un petit nitpick: vous dites "Il n’existe pas de langage lent ou rapide" puis "Python lui-même est horriblement lent", mais je suppose que vous vouliez dire l’interprète Python.
StrugglingProgrammer
2
@benalbrecht Mon point de vue est qu'il n'y a pas un seul bon ensemble de telles métriques. C'est un compromis, toujours. Si vous construisez des pilotes de périphérique, vous voulez être correct avant tout. Si vous construisez la colonne vertébrale de Twitter, vous voulez être efficace avant tout. Dans les deux cas, vous utilisez les outils et embauchez les personnes qui le permettent. Si vous êtes une entreprise en démarrage d'applications Android, vous utilisez ce que votre personnel sait et / ou ce qui minimise votre temps de mise sur le marché. Si vous enseignez des algorithmes, vous voulez un langage avec une syntaxe claire et concise et un passe-passe léger. Etc. Les priorités diffèrent et nous avons donc différentes langues.
Raphaël
23

Il y a une chose oubliée à propos de l'optimisation ici.

Il y avait un long débat sur la performance de Fortran par rapport à C. Mise à part un débat mal formé: le même code était écrit en C et en Fortran (comme le pensaient les testeurs) et les performances étaient testées sur la base des mêmes données. Le problème, c’est que ces langues diffèrent, C autorise le crénelage des pointeurs, contrairement à Fortran.

Donc les codes n'étaient pas les mêmes, il n'y avait pas de __restrict dans les fichiers testés en C, ce qui donnait des différences, après la réécriture des fichiers pour indiquer au compilateur qu'il peut optimiser les pointeurs, les runtimes deviennent similaires.

Le point ici est que certaines techniques d'optimisation sont plus faciles (ou commencent à être légales) dans un langage nouvellement créé.

En outre , il est possible à long terme à VM avec JIT surperformer C. Il existe deux possibilités:
code JIT peut profiter de la machine qu'il l' héberge (par exemple , certains SSE ou une autre exclusive pour certaines instructions CPU vectorisé) qui ne sont pas mis en œuvre programme comparé.X

Deuxièmement, VM peut effectuer un test de pression en cours d’exécution, afin de prendre le code sous pression et de l’optimiser ou même le précalculer au cours de l’exécution. À l’avance, le programme C compilé ne s’attend pas à savoir où est la pression ni (la plupart du temps), il existe des versions génériques d’exécutables pour la famille générale de machines.

Dans ce test, il existe également JS, ainsi que des machines virtuelles plus rapides que V8, et il est également plus rapide que C dans certains tests.

Je l'ai vérifié, et il y avait des techniques d'optimisation uniques non encore disponibles dans les compilateurs C.

Le compilateur C devrait faire une analyse statique de tout le code à la fois, marcher sur une plateforme donnée et contourner les problèmes d’alignement de la mémoire.

VM a simplement translittéré une partie du code pour optimiser l’assemblage et l’exécuter.

À propos de Julia - comme je l’ai vérifié, cela fonctionne sur du code AST, par exemple, GCC a ignoré cette étape et a récemment commencé à prendre des informations à partir de là. Ceci, ajouté à d’autres contraintes et techniques de machine virtuelle, pourrait en expliquer un peu.

Exemple: prenons une boucle simple, qui prend le point final de fin à partir de variables et charge une partie des variables dans les calculs connus au moment de l'exécution.

Le compilateur C génère des variables de chargement à partir de registres.
Mais au moment de l'exécution, ces variables sont connues et traitées comme des constantes lors de l'exécution.
Ainsi, au lieu de charger des variables à partir de registres (et de ne pas effectuer la mise en cache car cela peut changer, et d'après l'analyse statique, ce n'est pas clair), elles sont traitées intégralement comme des constantes et repliées et propagées.

Mal
la source
12

Les réponses précédentes donnent à peu près l'explication, bien que principalement d'un point de vue pragmatique, dans la mesure où la question a un sens , comme l'a parfaitement expliqué la réponse de Raphaël .

Ajoutant à cette réponse, il convient de noter que, de nos jours, les compilateurs C sont écrits en C. Bien sûr, comme le note Raphael, leur sortie et ses performances peuvent dépendre, entre autres choses, du processeur sur lequel il est exécuté. Mais cela dépend aussi de la quantité d'optimisation effectuée par le compilateur. Si vous écrivez en C un meilleur compilateur d'optimisation pour C (que vous compilez ensuite avec l'ancien pour pouvoir l'exécuter), vous obtenez un nouveau compilateur qui fait du C un langage plus rapide qu'auparavant. Alors, quelle est la vitesse de C? Notez que vous pouvez même compiler le nouveau compilateur avec lui-même, en tant que deuxième passe, afin de compiler plus efficacement, tout en fournissant le même code objet. Et le théorème du plein emploi montre qu’il n’ya pas de fin à de telles améliorations (merci à Raphaël pour le pointeur).

Mais je pense qu'il peut être intéressant d'essayer de formaliser la question, car elle illustre très bien certains concepts fondamentaux, notamment la vision dénotationnelle ou opérationnelle des choses.

Qu'est-ce qu'un compilateur?

Un compilateur , abrégé en s'il n'y a pas d'ambiguïté, est la réalisation d'une fonction calculable qui traduira un texte de programme calculant une fonction , écrit dans une langue source dans le texte du programme écrit dans une langue cible , qui est censé calculer la même fonction .CSTCCSTP:SP SP:T TP

D'un point de vue sémantique, c'est-à-dire dénotant , peu importe comment cette fonction de compilation est calculée, c'est-à-dire quelle réalisation est choisie. Cela pourrait même être fait par un oracle magique. Mathématiquement, la fonction est simplement un ensemble de paires .CSTCST{(P:S,P:T)PSSPTT}

La fonction de compilation sémantique est correct si les deux et calculent la même fonction . Mais cette formalisation s'applique également à un compilateur incorrect. Le seul point est que tout ce qui est mis en œuvre aboutit au même résultat indépendamment des moyens utilisés. Ce qui compte sémantiquement, c’est ce qui est fait par le compilateur, et non comment (et à quelle vitesse) cela est fait.CSTPSPTP

Obtenir réellement de est un problème opérationnel , qui doit être résolu. C'est pourquoi la fonction de compilation doit être une fonction calculable. Alors, toute langue avec le pouvoir de Turing, peu importe sa lenteur, sera certainement en mesure de produire un code aussi efficace que toute autre langue, même si elle le fait moins efficacement.P:TP:SCST

En affinant l’argument, nous voulons probablement que le compilateur ait une bonne efficacité, afin que la traduction puisse être effectuée dans un délai raisonnable. La performance du programme de compilation est donc importante pour les utilisateurs, mais elle n’a aucun impact sur la sémantique. Je parle de performance, car la complexité théorique de certains compilateurs peut être bien supérieure à ce à quoi on pourrait s’attendre.

À propos de l'amorçage

Cela illustrera la distinction et montrera une application pratique.

Il est maintenant courant de commencer par implémenter un langage avec un interprète , puis d’écrire un compilateur dans le langage lui-même. Ce compilateur peut être exécuté avec l'interpréteur pour traduire tout programme en programme . Nous avons donc un compilateur en cours d’exécution du langage au langage (machine?) , mais il est très lent, ne serait-ce que parce qu’il s’appuie sur un interprète.I S C S TSIS S C S TCST:SS I S P : S P : T STCST:SISP:SP:TST

Mais vous pouvez utiliser cette fonction de compilation pour compiler le compilateur , car il est écrit en langage , et vous obtenez ainsi un compilateur écrit en la langue cible . Si vous assumez, comme souvent le cas, que est une langue qui est plus efficacement interprété (natif de la machine, par exemple), vous obtenez une version plus rapide de votre compilateur directement dans le langage courant . Il fait exactement le même travail (c.-à-d. Produit les mêmes programmes cibles), mais le fait plus efficacement. S C S TCST:SS TTTCST:TTTT

babou
la source
"Ce qui compte sur le plan sémantique, c'est ce qui est fait, pas comment (et à quelle vitesse) cela est fait" - il convient de mentionner que des critères non fonctionnels existent dans la pratique. Il existe de nombreux programmes cibles équivalents du point de vue fonctionnel, mais nous pouvons en préférer certains pour d'autres raisons (efficacité, taille, meilleur alignement de la mémoire, ...). En d'autres termes, la vue d'un compilateur en tant que fonction définie par vous est plus limitée que nous le souhaiterions (elle ignore souvent les effets secondaires, tels que les E / S, par exemple). Cependant, cela sert le but explicatif que vous voulez.
Raphaël
@Raphael En ce qui concerne le théorème de plein emploi, je l'avais en tête (dans mon commentaire sur C), mais je ne connaissais pas le nom, et ai reporté la recherche d'une référence. Merci de l'avoir fait. --- La sémantique dont je parle est celle du compilateur, pas celle du programme cible. Le programme cible est préservé syntaxiquement et opérationnellement, pas seulement sémantiquement. Ou ai-je mal compris votre remarque? J'ai édité pour rendre les choses plus précises dans mon texte.
Babou
@ Raphaël Puisque vous n'avez pas supprimé votre commentaire, cela signifie-t-il que je l'ai mal compris ou que je n'y ai pas répondu correctement? Comment se fait-il que la vue du compilateur (et non du programme compilé) en tant que fonction soit trop limitée, d'un point de vue sémantique. Bien entendu, en tant que fonction, cela pourrait prendre d'autres arguments que le programme compilé, comme les directives d'optimisation), mais il s'agit d'un détail qui ne changerait pas la discussion.
Babou
Je pense que mon commentaire est un indicateur sur "il n'y a pas que ce modèle". Ce que vous écrivez n'est pas faux, mais ce n'est pas tout. Théoriquement, cela semble évident: "la" fonction de compilateur n’est pas définie en soi, car il existe une infinité de programmes cibles possibles, tous sémantiquement équivalents. Le choix est une partie très importante de la conception des compilateurs.
Raphaël
@ Raphaël Il n'y a que ce modèle. Vous avez mal interprété mon texte: je dis "une fonction calculable", car d'autres sont possibles. Le compilateur implémente une fonction de compilation spécifique "curly ", fonction de la syntaxe S à la syntaxe T, définissant un ensemble précis de paires syntaxiques de programmes (lettres en italique). Ces paires sont supposées calculer la même fonction "curly ", mais le second composant ne peut pas être remplacé par un autre avec la même propriété. J'ignore intentionnellement la sémantique des programmes pour que les choses restent comparables dans les processus de traduction. PCP
babou
6

Selon le théorème d'accélération de Blum, certains programmes écrits et exécutés sur la combinaison ordinateur / compilateur la plus rapide s'exécutent plus lentement qu'un programme identique sur votre premier PC exécutant BASIC interprété. Il n'y a tout simplement pas de "langage le plus rapide". Tout ce que vous pouvez dire, c’est que si vous écrivez le même algorithme dans plusieurs langages (implémentations; comme indiqué, il existe de nombreux compilateurs C, et j’ai même rencontré un interpréteur C assez capable), il fonctionnera plus vite ou plus lentement dans chaque langage. .

Il ne peut y avoir de hiérarchie "toujours plus lente". C’est un phénomène connu de tous les gens qui maîtrisent plusieurs langues: chaque langage de programmation a été conçu pour un type d’application spécifique et les implémentations les plus utilisées ont été amoureusement optimisées pour ce type de programme. Je suis à peu près sûr qu'un programme permettant de bricoler avec des chaînes écrites en Perl battra probablement le même algorithme que celui écrit en C, alors qu'un programme grignotant de grands tableaux d'entiers en C sera plus rapide que Perl.

vonbrand
la source
1
"Chaque langage de programmation a été conçu pour un type d’application spécifique" En fait, la plupart des langages de programmation que les gens utilisent réellement sont des langages à usage général, au contraire de ceux conçus pour des applications spécifiques. C'est juste que certaines langues finissent par être davantage utilisées dans certains domaines, principalement en raison d'effets sociaux.
Cubique
Je suppose que cela dépend de la largeur que vous interprétez le terme "type d'application spécifique". Il est vrai que la plupart des langues traditionnelles ne sont pas DSLs, ils certainement sont conçus avec certaines utilisations à l' esprit. C a été conçu pour implémenter Unix. Java a été conçu pour écrire des scripts sur les téléviseurs interactifs. Smalltalk a été conçu pour enseigner aux enfants. ECMAScript a été conçu pour les scripts Web côté serveur et côté client. Perl a été conçu pour le traitement de texte et les scripts Unix. PHP a été conçu pour les scripts Web côté serveur. Erlang a été conçu pour la fiabilité. Scheme a été conçu pour explorer les…
Jörg W Mittag
… Fondements de OO et du modèle d'acteur. APL a été conçu comme une notation pour l’enseignement des mathématiques. Julia a été conçue pour la programmation scientifique. Bien sûr, toutes ces langues sont maintenant utilisées en dehors de leur domaine de problèmes d'origine, mais il existe encore certaines propriétés qui les rendent plus ou moins adaptées à certains types d'applications, même si elles peuvent toutes être utilisées pour créer toutes sortes de logiciels. des choses.
Jörg W Mittag Le
4

Revenons à la ligne d'origine: "Comment un langage dont le compilateur est écrit en C peut-il être plus rapide que C?" Je pense que cela voulait vraiment dire: comment un programme écrit en Julia, dont le cœur est écrit en C, peut-il être plus rapide qu'un programme écrit en C? En particulier, comment le programme "mandel", tel qu’écrit en Julia, peut-il s’exécuter dans 87% du temps d’exécution du programme "mandel" équivalent écrit en C?

Le traité de Babou est la seule réponse correcte à cette question jusqu'à présent. Jusqu'à présent, toutes les autres réponses répondent plus ou moins à d'autres questions. Le problème avec le texte de babou est que la description théorique de "Qu'est-ce qu'un compilateur", longue de plusieurs paragraphes, est rédigée dans des termes que l'affiche originale aura probablement du mal à comprendre. Quiconque saisit les concepts mentionnés par les mots "sémantique", "dénotationellement", "réalisation", "calculable", etc., connaît déjà la réponse à la question.

La réponse la plus simple est que ni le code C ni le code de Julia ne sont directement exécutables par la machine. Les deux doivent être traduits, et ce processus de traduction introduit de nombreuses manières pour que le code machine exécutable puisse être plus lent ou plus rapide, tout en produisant le même résultat final. C et Julia font tous les deux une compilation, ce qui signifie une série de traductions sous une autre forme. Généralement, un fichier texte lisible par un humain est traduit en une représentation interne, puis écrit sous la forme d'une séquence d'instructions que l'ordinateur peut comprendre directement. Avec certaines langues, cela ne se résume pas à cela, et Julia en fait partie: elle dispose d'un compilateur "JIT", ce qui signifie que le processus de traduction ne doit pas nécessairement se dérouler en même temps pour l'ensemble du programme. Mais le résultat final pour toute langue est un code machine qui ne nécessite aucune autre traduction, code qui peut être envoyé directement à la CPU pour lui faire faire quelque chose. À la fin, CECI est le "calcul", et il existe plus d’une façon de dire à un processeur comment obtenir la réponse que vous souhaitez.

On pourrait imaginer un langage de programmation comportant à la fois un opérateur "plus" et un opérateur "multipliant", et un autre langage n'ayant que "plus". Si votre calcul nécessite une multiplication, une langue sera "plus lente" car bien sûr, le processeur peut faire les deux directement, mais si vous n'avez aucun moyen d'exprimer le besoin de multiplier 5 * 5, il vous reste à écrire "5 + 5 + 5 + 5 + 5 ". Ce dernier prendra plus de temps pour arriver à la même réponse. Vraisemblablement, il y a une partie de ce qui se passe avec Julia; peut-être que le langage permet au programmeur d’énoncer l’objectif souhaité du calcul d’un ensemble de Mandelbrot d’une manière qu’il n’est pas possible d’exprimer directement en C.

Le processeur utilisé pour le test de référence était répertorié en tant que processeur Xeon E7-8850 2,00 GHz. Le benchmark C a utilisé le compilateur gcc 4.8.2 pour produire des instructions pour ce processeur, tandis que Julia a utilisé le framework de compilateur LLVM. Il est possible que le backend de gcc (la partie qui produit le code machine pour une architecture de processeur particulière) ne soit pas aussi avancé que le backend de LLVM. Cela pourrait faire une différence dans les performances. Il y a aussi beaucoup d'autres choses en cours - le compilateur peut "optimiser" en émettant peut-être des instructions dans un ordre différent de celui spécifié par le programmeur, ou même en ne faisant rien du tout s'il peut analyser le code et déterminer qu'il ne l'est pas. nécessaire pour obtenir la bonne réponse. Et le programmeur aurait peut-être écrit une partie du programme C de manière à le ralentir, mais ne l'a pas fait.

Toutes ces choses sont des façons de dire: il existe de nombreuses façons d'écrire du code machine pour calculer un ensemble de Mandelbrot, et le langage que vous utilisez a un effet majeur sur la façon dont le code machine est écrit. Plus vous en comprendrez sur la compilation, les jeux d'instructions, les caches, etc., mieux vous serez en mesure d'obtenir les résultats souhaités. Le principal résultat tiré des résultats de référence cités pour Julia est qu’aucun langage ou outil n’est meilleur dans tous les domaines. En fait, le meilleur facteur de vitesse de l’ensemble du graphique était celui de Java!

Randall Krieg
la source
2

La vitesse d'un programme compilé dépend de deux choses:

  1. Les performances de la machine qui l'exécute
  2. Le contenu du fichier exécutable

Le langage dans lequel un compilateur est écrit est sans importance pour (1). Par exemple, un compilateur Java peut être écrit en C, Java ou Python, mais dans tous les cas, la "machine" qui exécute le programme est la machine virtuelle Java.

Le langage dans lequel un compilateur est écrit est sans importance pour (2). Par exemple, il n'y a aucune raison pour qu'un compilateur C écrit en Python ne puisse pas produire exactement le même fichier exécutable qu'un compilateur C écrit en C ou en Java.

Artelius
la source
1

Je vais essayer d'offrir une réponse plus courte.

Le coeur de la question réside dans la définition de la "vitesse" d'une langue .

La plupart des tests de comparaison de vitesse, sinon tous, ne testent pas la vitesse maximale possible. Au lieu de cela, ils écrivent un petit programme dans une langue qu'ils veulent tester pour résoudre un problème. Lors de l’écriture du programme, le programmeur utilise ce qu’ils supposent * être les meilleures pratiques et conventions du langage, au moment du test. Ensuite, ils mesurent la vitesse à laquelle le programme a été exécuté.

* Les hypothèses sont parfois fausses.

Peter
la source
0

Le code écrit dans un langage X dont le compilateur est écrit en C peut surperformer un code écrit en C, à condition que le compilateur C fasse une optimisation médiocre par rapport à celle du langage X. Si nous gardons l'optimisation en discussion, alors si le compilateur de X pourrait générer mieux code objet que celui généré par le compilateur C, le code écrit en X peut également gagner la course.

Mais si le langage X est un langage interprété et que l'interpréteur est écrit en C, et si nous supposons que l'interpréteur du langage X et du code écrit en C est compilé par le même compilateur C, le code écrit en X ne surpassera en rien le code écrit en C, à condition que les deux implémentations suivent le même algorithme et utilisent des structures de données équivalentes.

Jayraj
la source
2
Qu'est-ce que cela ajoute par rapport aux réponses précédentes? En outre, je ne pense pas que votre deuxième paragraphe soit vrai, pour des raisons exposées dans d'autres réponses.
Raphaël