En écoutant le podcast StackOverflow, le jab n'arrête pas de dire que les "vrais programmeurs" écrivent en C et que C est tellement plus rapide parce qu'il est "proche de la machine". Laissant l'ancienne affirmation pour un autre poste, quelle est la particularité de C qui lui permet d'être plus rapide que d'autres langues? Autrement dit: qu'est-ce qui empêche les autres langages de compiler en binaire aussi rapidement que C?
Il n'y a pas grand-chose de particulier à propos de C. C'est l'une des raisons pour lesquelles c'est rapide.
Les nouvelles langues qui prennent en charge garbage collection _ _ _ { typage dynamique } _ _ ainsi que d'autres fonctionnalités facilitant l'écriture de programmes par le programmeur.
Le problème, c’est qu’un surcroît de temps de traitement entraîne une dégradation des performances de l’application. C n’a rien de tout cela, ce qui signifie qu’il n’ya pas de surcharge, mais que le programmeur doit pouvoir allouer de la mémoire et le libérer pour éviter fuites de mémoire , et doit s’occuper du typage statique. de variables.
Cela dit, de nombreux langages et plates-formes, tels que Java (avec sa machine virtuelle Java ) et .NET (avec son Common Language Runtime) ont amélioré les performances au fil des ans avec des avancées telles que just-in). -time compilation qui produit du code machine natif à partir de bytecode pour améliorer les performances.
Les concepteurs C ont fait un compromis. C'est-à-dire qu'ils ont pris la décision de placer la vitesse au-dessus de la sécurité. C ne sera pas
Lorsque vous indexez dans un tableau, en Java, il faut un appel de méthode dans la machine virtuelle, une vérification des liens et d’autres vérifications de cohérence. Ce est valide et tout à fait correct} _, car il ajoute une sécurité là où il est dû. Mais en C, même les choses les plus triviales ne sont pas mises en sécurité. Par exemple, C n’a pas besoin de memcpy pour vérifier si les régions à copier se chevauchent. C'est pas conçu comme un langage permettant de programmer une application professionnelle.
Mais ces décisions de conception sont pas des bogues dans le langage C}. Ils sont conçus pour permettre aux compilateurs et aux rédacteurs de bibliothèques de tirer le meilleur parti des performances de l’ordinateur. Voici l'esprit de C comment le C Reason document l'explique:
Le code C peut ne pas être portable. Même s’il s’est efforcé de donner aux programmeurs l’opportunité d’écrire des programmes véritablement portables, le Comité n’a pas voulu contraindre les programmeurs à écrire de manière portable, afin d’empêcher l’utilisation de C comme "assembleur de haut niveau": la possibilité d’écrire Le code spécifique est l’un des atouts de C.
Conserver l’esprit de C. Le comité a pour objectif principal de préserver l’esprit traditionnel de C. Il existe de nombreuses facettes de l’esprit de C, mais l’essentiel est un sentiment communautaire des principes sous-jacents la langue est basée. Certaines des facettes de l’esprit de C peuvent être résumées comme suit:
- Faites confiance au programmeur.
- N'empêchez pas le programmeur de faire ce qui doit être fait.
- Gardez la langue petite et simple.
- Fournissez une seule façon de faire une opération.
- Faites vite, même s'il n'est pas garanti d'être portable.
Le dernier proverbe a besoin d'une petite explication. Le potentiel d’une génération de code efficace est l’un des atouts les plus importants de C. Pour éviter toute explosion de code dans une opération qui semble être très simple, de nombreuses opérations sont définies de la manière dont le matériel de la machine cible une règle abstraite générale. Un exemple de cette volonté de vivre avec ce que fait la machine peut être vu dans les règles qui régissent l'élargissement des objets char à utiliser dans les expressions: si les valeurs des objets char s'élargissent à des quantités signées ou non signées, dépend généralement de l'opération d'octet utilisée. efficace sur la machine cible.
Si vous passez un mois à créer quelque chose en C qui s'exécute en 0,05 seconde et que je passe une journée à écrire la même chose en Java et que cela s'exécute en 0,10 seconde, le C est-il vraiment plus rapide?
Mais pour répondre à votre question, le code C bien écrit s'exécutera généralement plus rapidement que le code bien écrit dans d'autres langues, car l'écriture de code C "bien" inclut la réalisation d'optimisations manuelles à un niveau proche de celui de la machine.
Bien que les compilateurs soient très intelligents, ils ne sont pas encore en mesure de créer de manière créative un code qui concurrence les algorithmes massés à la main (en supposant que les "mains" appartiennent à un programmeur good C).
Modifier:
Beaucoup de commentaires vont dans le sens de "J'écris en C et je ne pense pas aux optimisations."
Mais prenons un exemple spécifique de ce post :
À Delphi, je pourrais écrire ceci:
function RemoveAllAFromB(a, b: string): string;
var
before, after :string;
begin
Result := b;
if 0 < Pos(a,b) then begin
before := Copy(b,1,Pos(a,b)-Length(a));
after := Copy(b,Pos(a,b)+Length(a),Length(b));
Result := before + after;
Result := RemoveAllAFromB(a,Result); //recursive
end;
end;
et en C j'écris ceci:
char *s1, *s2, *result; /* original strings and the result string */
int len1, len2; /* lengths of the strings */
for (i = 0; i < len1; i++) {
for (j = 0; j < len2; j++) {
if (s1[i] == s2[j]) {
break;
}
}
if (j == len2) { /* s1[i] is not found in s2 */
*result = s1[i];
result++; /* assuming your result array is long enough */
}
}
Mais combien d’optimisations existe-t-il dans la version C? Nous prenons beaucoup de décisions concernant l'implémentation auxquelles je ne pense pas dans la version Delphi. Comment une chaîne est-elle implémentée? À Delphes, je ne le vois pas. En C, j'ai décidé que ce serait un pointeur sur un tableau d'entiers ASCII, que nous appelons des caractères. En C, nous testons l'existence des personnages un à un. En Delphi, j'utilise Pos.
Et ce n’est qu’un petit exemple. Dans un grand programme, un programmeur C doit prendre ce type de décisions de bas niveau avec quelques lignes de code. Il s’agit d’un exécutable conçu à la main et optimisé à la main.
Je ne l'ai pas déjà vu, je vais donc le dire: C a tendance à être plus rapide car tout le reste est écrit en C.
Java est construit sur C, Python est construit sur C (ou Java, ou .NET, etc.), Perl, etc. Le système d’exploitation est écrit en C, les machines virtuelles sont écrites en C, les compilateurs sont écrits en C, les interprètes sont écrits en C. Certaines choses sont encore écrites en langage d'assemblage, ce qui a tendance à être encore plus rapide. De plus en plus de choses sont écrites dans autre chose, elle-même écrite en C.
Chaque instruction que vous écrivez dans d'autres langues (et non pas Assembly) est généralement implémentée sous la forme de plusieurs instructions en C, qui sont compilées en code machine natif. Etant donné que ces autres langues ont tendance à exister pour obtenir un niveau d'abstraction plus élevé que C, les déclarations supplémentaires requises en C ont tendance à être axées sur le renforcement de la sécurité, la complexité et le traitement des erreurs. Ce sont souvent de bonnes choses, mais elles ont un cost, et ses noms sont speed et size.
Personnellement, j’ai écrit littéralement dans des douzaines de langues couvrant la majeure partie du spectre disponible, et j’ai personnellement recherché la magie à laquelle vous faites allusion:
Comment puis-je avoir mon gâteau et le manger aussi? Comment puis-je jouer avec des abstractions de haut niveau dans ma langue préférée, puis descendre au cœur de la vitesse de C?
Après quelques années de recherche, ma réponse est Python (sur C). Vous voudrez peut-être y jeter un coup d'œil. En passant, vous pouvez également accéder à Assembly à partir de Python (avec une aide mineure d’une bibliothèque spéciale).
D'autre part, un code incorrect peut être écrit dans n'importe quelle langue . Par conséquent, le code C (ou d'assemblage) n'est pas automatiquement plus rapide. De même, certaines astuces d’optimisation peuvent rapprocher le parties du code de langage de niveau supérieur du niveau de performance du C brut. Mais, pour la plupart des applications, votre programme passe le plus clair de son temps à attendre des personnes ou du matériel. la différence n'a pas d'importance.
Prendre plaisir.
Il y a beaucoup de questions là-dedans - la plupart pour lesquelles je ne suis pas qualifié pour répondre. Mais pour ce dernier:
qu'est-ce qui empêche les autres langues de compiler en binaire, aussi vite que C?
Dans un mot, abstraction.
C n’est qu’un ou deux niveaux d’abstraction en dehors du langage machine. Les langages Java et .Net ont au moins 3 niveaux d'abstraction loin de l'assembleur. Je ne suis pas sûr de Python et Ruby.
En règle générale, plus il y a de jouets de programmation (types de données complexes, etc.), plus on s'éloigne du langage machine et plus la traduction est complexe.
Je suis ici et là mais c'est le Gist de base.
Mise à jour ------- Il y a quelques bons commentaires sur ce post avec plus de détails.
Ce n’est pas tant que C est rapide que le modèle de coûts de C est transparent. Si un programme C est lent, il est lent: en exécutant de nombreuses instructions. Par rapport au coût des opérations en C, les opérations de haut niveau sur des objets (en particulier la réflexion) ou des chaînes peuvent avoir des coûts qui ne sont pas évidents.
Deux langages généralement compilés en binaires et aussi rapides que C sont Standard ML (utilisant le compilateur MLton ) et Objective Caml . Si vous consultez la jeu de tests , vous constaterez que pour certains tests, comme les arbres binaires, la version OCaml est plus rapide que C. (Je n'ai trouvé aucune entrée MLton.) Mais ne prenez pas la fusillade trop au sérieux; Comme il est dit, il s’agit d’un jeu. Les résultats reflètent souvent l’effort fourni par les utilisateurs pour ajuster le code.
C n'est pas toujours plus rapide.
C est plus lent que, par exemple, le Fortran moderne.
C est souvent plus lent que Java pour certaines choses. (Surtout après que le compilateur JIT ait essayé votre code)
C permet de créer des alias de pointeur, ce qui signifie que de bonnes optimisations ne sont pas possibles. En particulier lorsque vous avez plusieurs unités d'exécution, cela provoque des blocages d'extraction de données. Ow.
L’hypothèse selon laquelle l’arithmétique des pointeurs fonctionne réellement provoque des performances ralenties sur certaines familles de processeurs (PIC notamment)!.
En gros, lorsque vous obtenez une unité vectorielle ou un compilateur paralléliseur, C pue et le Fortran moderne s'exécute plus rapidement.
Les astuces du programmeur C telles que thunking (modification de l'exécutable à la volée) provoquent des blocages de prélecture CPU.
Vous avez la dérive?
Et notre bon ami, le x86, exécute un jeu d’instructions qui, de nos jours, n’a guère de rapport avec l’architecture du processeur. Registres d’ombre, optimiseurs de chargement, tout dans la CPU. Donc C est alors proche du métal virtuel. Le vrai métal, Intel ne vous laisse pas voir. (Historiquement, les processeurs VLIW étaient un peu fous, alors peut-être que ce n'est pas si grave.)
Si vous programmez en C sur un DSP hautes performances (peut-être un DSP TI?), Le compilateur doit effectuer des tâches délicates pour dérouler le C sur plusieurs unités d'exécution parallèles. Donc dans ce cas, C n’est pas proche du métal, mais du compilateur, qui optimisera tout le programme. Bizarre.
Enfin, certains processeurs (www.ajile.com) exécutent des bytecodes Java matériellement. C serait un PITA à utiliser sur ce processeur.
qu'est-ce qui empêche d'autres langues de être capable de compiler en binaire ça court aussi vite que C?
Rien. Les langages modernes tels que Java ou .NET langs sont davantage axés sur la productivité du programmeur que sur les performances. Le matériel est bon marché maintenant. De plus, la compilation en représentation intermédiaire donne de nombreux bonus tels que la sécurité, la portabilité, etc. Le CLR .NET peut tirer parti de différents matériels. Par exemple, vous n'avez pas besoin d'optimiser/recompiler manuellement le programme pour utiliser le jeu d'instructions SSE.
Les principaux facteurs sont qu’il s’agit d’un langage à typage statique, compilé en code machine. De plus, puisqu'il s'agit d'un langage de bas niveau, il ne fait généralement rien que vous ne lui disiez pas.
Ce sont quelques autres facteurs qui me viennent à l’esprit.
La plupart des langages à caractères statiques peuvent être compilés aussi rapidement ou plus rapidement que C, en particulier s'ils peuvent émettre des hypothèses que C ne peut pas faire à cause du crénelage de pointeur, etc.
Je suppose que vous avez oublié que la langue de l'Assemblée est aussi une langue :)
Mais sérieusement, les programmes C ne sont plus rapides que lorsque le programmeur sait ce qu’il fait. Vous pouvez facilement écrire un programme C dont l’exécution est plus lente que les programmes écrits dans d’autres langues faisant le même travail.
C est plus rapide parce qu’il est conçu de cette façon. Cela vous permet de faire beaucoup de choses "de bas niveau" qui aident le compilateur à optimiser le code. Ou, dirons-nous, vous êtes le programmeur responsable de l'optimisation du code. Mais c'est souvent assez compliqué et sujet aux erreurs.
D'autres langues, comme d'autres déjà mentionnées, sont davantage axées sur la productivité du programmeur. Il est communément admis que le temps de programmation est beaucoup plus coûteux que le temps de machine (même à l’époque). Il est donc logique de minimiser le temps que les programmeurs consacrent à l’écriture et au débogage de programmes au lieu de la durée d’exécution. Pour ce faire, vous sacrifierez un peu ce que vous pouvez faire pour rendre le programme plus rapide car beaucoup de choses sont automatisées.
C++ est plus rapide en moyenne (comme c'était à l'origine un super ensemble de C). Cependant, pour des repères spécifiques, il existe souvent une autre langue qui est plus rapide.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/
fannjuch-redux
était le plus rapide à Scala
n-body
et fasta
étaient plus rapides à Ada.
spectral-norm
était le plus rapide à Fortran.
reverse-complement
, mandelbrot
et pidigits
étaient les plus rapides en ATS.
regex-dna
était le plus rapide en JavaScript.
chameneou-redux
était le plus rapide est Java 7.
thread-ring
était le plus rapide à Haskell.
Les autres points de repère étaient les plus rapides en C ou C++.
Je ne pense pas que quiconque ait mentionné le fait que beaucoup plus d'efforts ont été consacrés aux compilateurs C qu'à tout autre compilateur, à l'exception peut-être de Java.
C est extrêmement optimable pour beaucoup des raisons déjà mentionnées - plus que presque toutes les autres langues. Donc, si le même effort est fait sur les compilateurs d’autres langages, C sera probablement toujours en tête.
Je pense qu’il existe au moins un langage candidat qui, avec l’effort, pourrait être optimisé mieux que le C et nous pourrions donc voir des implémentations produisant des binaires plus rapides. Je pense à Digital Mars D parce que le créateur a eu soin de créer un langage potentiellement mieux optimisé que le C. Il existe peut-être d'autres langages offrant cette possibilité. Cependant, je ne peux pas imaginer que les langages aient des compilateurs plus que quelques pour cent plus rapides que les meilleurs compilateurs C. J'aimerais avoir tort.
Je pense que le vrai "fruit à portée de main" sera dans des langues conçues pour être FACILES à optimiser par les humains. Un programmeur expérimenté peut accélérer toutes les langues - mais vous devez parfois faire des choses ridicules ou utiliser des constructions non naturelles pour y parvenir. Bien que cela demande toujours des efforts, un bon langage devrait produire un code relativement rapide sans être obsédé par la façon dont le programme est écrit.
Il est également important (au moins pour moi) que le code du cas le plus défavorable ait tendance à être rapide. Il existe de nombreuses "preuves" sur le Web indiquant que Java est aussi rapide ou aussi rapide que C, mais cela est basé sur des exemples de sélection. Je ne suis pas un grand fan de C, mais je sais que TOUT ce que j'écris en C fonctionnera bien. Avec Java, il fonctionnera "probablement" à moins de 15% de la vitesse, généralement à moins de 25%, mais dans certains cas, cela peut être bien pire. Tous les cas où cela se produit tout aussi rapidement ou à quelques pour cent sont généralement dus au fait que la plupart du temps est passé dans le code de bibliothèque, qui est de toute façon fortement optimisé.
C'est en fait un peu un mensonge perpétué. Même s’il est vrai que les programmes C sont souvent plus rapides, ce n’est pas toujours le cas, surtout si le programmeur C n’est pas très bon.
Les gens ont tendance à oublier que le programme doit bloquer certaines entrées/sorties, telles que la saisie de l'utilisateur dans un programme d'interface graphique. Dans ces cas, la langue que vous utilisez importe peu car vous êtes limité par la vitesse à laquelle les données peuvent arriver plutôt que par la vitesse à laquelle vous pouvez la traiter. Dans ce cas, peu importe si vous utilisez C, Java, C # ou même Perl; vous ne pouvez tout simplement pas aller plus vite que les données peuvent entrer.
L'autre problème majeur est que l'utilisation de la récupération de place, et non de l'utilisation de pointeurs appropriés, permet à la machine virtuelle de rendre un certain nombre d'optimisations non disponibles dans d'autres langues. Par exemple, la machine virtuelle Java est capable de déplacer des objets sur le tas pour le défragmenter. Cela rend les allocations futures beaucoup plus rapides, car le prochain index peut simplement être utilisé plutôt que de le rechercher dans une table. Les machines virtuelles modernes n’ont pas non plus besoin de désallouer la mémoire; au lieu de cela, ils déplacent simplement les objets vivants lorsqu’ils effectuent une GC et la mémoire épuisée des objets morts est récupérée essentiellement gratuitement.
Cela soulève également un point intéressant à propos de C et plus encore en C++. Il existe en quelque sorte une philosophie de conception consistant à "Si vous n'en avez pas besoin, vous ne payez pas pour cela." Le problème est que si vous le voulez, vous finissez par payer le prix fort. Par exemple, l'implémentation de vtable en Java a tendance à être bien meilleure que celle de C++, les appels de fonctions virtuelles sont donc beaucoup plus rapides. D'autre part, vous n'avez pas d'autre choix que d'utiliser des fonctions virtuelles en Java et elles coûtent toujours quelque chose, mais dans les programmes qui utilisent beaucoup de fonctions virtuelles, le coût réduit s'additionne.
Bon nombre de ces réponses donnent des raisons valables pour lesquelles C est, ou n'est pas, plus rapide (en général ou dans des scénarios spécifiques). C'est indéniable que:
Malgré tout, il y a quelque chose d'autre qui, à mon avis, affecte les performances comparées de C par rapport à de nombreuses autres langues plus que tout autre facteur. En être témoin:
D'autres langues facilitent souvent l'écriture de code qui s'exécute plus lentement. Souvent, cela est même encouragé par les conceptions philosophiques de la langue. Corollaire: un programmeur C est plus susceptible d'écrire du code qui n'effectue pas d'opérations inutiles.
A titre d'exemple, considérons un programme Windows simple dans lequel une seule fenêtre principale est créée. Une version C devrait renseigner une structure WNDCLASS[EX]
qui serait passée à RegisterClass[Ex]
, puis appeler CreateWindow[Ex]
et entrer une boucle de message. Le code très simplifié et abrégé est le suivant:
WNDCLASS wc;
MSG msg;
wc.style = 0;
wc.lpfnWndProc = &WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "MainWndCls";
RegisterClass(&wc);
CreateWindow("MainWndCls", "", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Un programme équivalent en C # pourrait ne contenir qu'une seule ligne de code:
Application.Run(new Form());
Cette ligne de code fournit toutes les fonctionnalités de près de 20 lignes de code C et ajoute certaines choses que nous avons omises, telles que la vérification des erreurs. La bibliothèque la plus riche et la plus complète (comparée à celles utilisées dans un projet C typique) a beaucoup travaillé pour nous, ce qui nous a permis de rédiger de nombreux autres extraits de code qui nous paraissent courts, mais qui impliquent de nombreuses étapes dans les coulisses.
Mais une riche bibliothèque permettant de gonfler le code facilement et rapidement n’est pas vraiment mon objectif. Mon point est plus évident lorsque vous commencez à examiner ce qui se passe réellement lorsque notre petit one-liner est exécuté. Pour vous amuser un peu de temps, activez l’accès aux sources .NET dans Visual Studio 2008 ou une version ultérieure, et passez à la simple ligne ci-dessus. L’un des joyaux amusants que vous rencontrerez est ce commentaire dans le getter pour Control.CreateParams
:
// In a typical control this is accessed ten times to create and show a control.
// It is a net memory savings, then, to maintain a copy on control.
//
if (createParams == null) {
createParams = new CreateParams();
}
Dix fois. Les informations à peu près équivalentes à la somme de ce qui est stocké dans une structure WNDCLASSEX
et de ce qui est passé à CreateWindowEx
sont extraites de la classe Control
dix fois avant et stockées dans une structure WNDCLASSEX
et transmises à RegisterClassEx
et CreateWindowEx
.
Dans l’ensemble, le nombre d’instructions exécutées pour exécuter cette tâche très élémentaire est de 2 à 3 ordres de grandeur de plus en C # qu'en C. Cela tient en partie à l’utilisation d’une bibliothèque riche en fonctionnalités, qui est nécessairement généralisée, par opposition à notre simple code C qui fait exactement ce dont nous avons besoin et rien de plus. Mais cela tient en partie au fait que la nature modulaire et orientée objet du .NET Framework se prête à de nombreuses répétitions d’exécution souvent évitées par une approche procédurale.
Je n'essaie pas de choisir C # ou le framework .NET. Je ne dis pas non plus que la modularisation, la généralisation, les fonctionnalités de bibliothèque/langage, la POO, etc. sont mauvaises choses. J'avais l'habitude de faire l'essentiel de mon développement en C, plus tard en C++ et plus récemment en C #. De même, avant C, j'utilisais principalement Assembly. Et à chaque étape "supérieure" de ma langue, j'écris de meilleurs programmes, plus maintenables, plus robustes en moins de temps. Ils ont toutefois tendance à exécuter un peu plus lentement.
Pour la plupart, chaque instruction C correspond à très peu d'instructions d'assembleur. Vous écrivez essentiellement du code machine de niveau supérieur, vous avez donc le contrôle sur presque tout ce que le processeur fait. De nombreux autres langages compilés, tels que C++, contiennent de nombreuses instructions simples qui peuvent se transformer en beaucoup plus de code que vous ne le pensez (fonctions virtuelles, constructeurs de copie, etc.). Les langages interprétés comme Java ou Ruby ont une autre couche de instructions que vous ne voyez jamais - la machine virtuelle ou l’interprète.
Il ne s'agit pas tant de la langue que des outils et des bibliothèques. Les bibliothèques et les compilateurs disponibles pour C sont beaucoup plus anciens que pour les nouveaux langages. Vous pourriez penser que cela les ralentirait, mais au contraire.
Ces bibliothèques ont été écrites à une époque où la puissance de traitement et la mémoire étaient rares. Ils devaient être écrits très efficacement pour fonctionner du tout. Les développeurs de compilateurs C ont également eu longtemps à travailler dans toutes sortes d'optimisations intelligentes pour différents processeurs. La maturité de C et sa large adoption constituent un avantage considérable par rapport aux autres langues du même âge. Cela confère également à C un avantage en termes de vitesse par rapport aux outils plus récents, qui ne mettent pas autant l'accent sur les performances brutes que C.
Le manque d’abstraction est ce qui rend C plus rapide. Si vous écrivez une déclaration de sortie, vous savez exactement ce qui se passe. Si vous écrivez une instruction de sortie en Java, celle-ci est compilée dans un fichier de classe qui est ensuite exécuté sur une machine virtuelle introduisant une couche d'abstraction. L'absence de fonctionnalités orientées objet dans le langage augmente également sa vitesse à générer moins de code. Si vous utilisez C comme langage orienté objet, vous codez pour des choses telles que les classes, l'inharitence, etc. Cela signifie plutôt que de créer quelque chose d'assez général pour tout le monde avec la quantité de code et la pénalité de performance qui vous obligent à écrire ce dont vous avez besoin pour faire le travail.
Le code courant le plus rapide serait un code machine soigneusement fabriqué à la main. L'assembleur sera presque aussi bon. Les deux sont de très bas niveau et il faut beaucoup d’écriture de code pour faire les choses. C est un peu au dessus de l'assembleur. Vous avez toujours la possibilité de contrôler les choses à un niveau très bas dans la machine réelle, mais il y a suffisamment d'abstraction pour que l'écrire soit plus rapide et plus facile que l'assembleur. D'autres langages tels que C # et Java sont encore plus abstraits. Alors que Assembler et le code machine sont appelés langages de bas niveau, C # et Java (et beaucoup d’autres) sont appelés langages de haut niveau. C est parfois appelé une langue de niveau intermédiaire.
Étonnant de voir l'ancien "C/C++ doit être plus rapide que Java parce que Java est interprété", le mythe est toujours vivant. Il y a des articles datant de quelques années , ainsi que des articles plus récents , qui expliquent par des concepts ou des mesures pourquoi/ ce n'est tout simplement pas toujours le cas .
Les implémentations actuelles de machines virtuelles (et pas seulement la machine virtuelle, à propos) peuvent tirer parti des informations recueillies au cours de l'exécution du programme pour ajuster le code de manière dynamique au fur et à mesure de son exécution, en utilisant diverses techniques:
et une variété d'autres ajustements basés sur la connaissance de ce que le code est en train de faire et sur les caractéristiques réelles de l'environnement dans lequel il est exécuté.
Je sais que beaucoup de gens l'ont dit d'une manière longue, mais:
C est plus rapide car il en fait moins (pour vous).
Au bon vieux temps, il n'y avait que deux types de langages: compilé et interprété.
Les langages compilés utilisaient un "compilateur" pour lire la syntaxe du langage et le convertir en un code de langage d'assemblage identique, ce qui pouvait être directement sur le CPU. Les langages interprétés utilisaient différents schémas, mais la syntaxe du langage était essentiellement convertie en une forme intermédiaire, puis exécutée dans un "interpréteur", un environnement permettant d'exécuter le code.
Ainsi, dans un sens, il y avait une autre "couche" - l'interprète - entre le code et la machine. Et, comme toujours dans les ordinateurs, plus de ressources sont utilisées. Les interprètes étaient plus lents, car ils devaient effectuer plus d'opérations.
Plus récemment, nous avons vu plus de langages hybrides comme Java, qui utilisent à la fois un compilateur et un interprète pour les faire fonctionner. C'est compliqué, mais une machine virtuelle Java est plus rapide, plus sophistiquée et beaucoup plus optimisée que les anciens interprètes. Elle offre donc un bien meilleur changement de performances (au fil du temps) plus proche du code directement compilé. Bien sûr, les nouveaux compilateurs ont également des astuces d'optimisation plus sophistiquées, de sorte qu'ils ont tendance à générer un code bien meilleur qu'avant. Mais la plupart des optimisations, le plus souvent (bien que pas toujours), font en sorte que certains types de compromis ne soient pas toujours plus rapides dans toutes les circonstances. Comme tout le reste, rien n'est gratuit, les optimiseurs doivent donc tirer leur fierté de quelque part (bien que, souvent, il utilise un processeur au moment de la compilation pour économiser le processeur d'exécution).
Pour revenir au C, il s’agit d’un langage simple, qui peut être compilé dans un assemblage assez optimisé, puis exécuté directement sur la machine cible. En C, si vous incrémentez un entier, il est fort probable qu'il ne s'agisse que d'une étape d'assembleur dans la CPU, mais en Java, cela pourrait être beaucoup plus que cela (et pourrait également inclure un peu de garbage collection: -) C vous offre une abstraction qui est bien plus proche de la machine (l’assembleur est le plus proche), mais vous devez faire beaucoup plus de travail pour le faire fonctionner et ce n’est pas aussi protégé, facile à utiliser ou facile à corriger. La plupart des autres langues vous offrent une abstraction plus élevée et prennent en charge davantage de détails sous-jacents, mais en échange de leurs fonctionnalités avancées, elles nécessitent plus de ressources. Lorsque vous généralisez certaines solutions, vous devez gérer un plus large éventail d’informatique, qui nécessite souvent davantage de ressources.
Paul.
Ne prenez pas Word pour cela, examinez le désassemblage de C et votre langue de choix dans toute partie critique de votre code en termes de performances. Je pense que vous pouvez simplement regarder dans la fenêtre de désassemblage au moment de l’exécution de Visual Studio pour voir le .Net désassemblé. Cela devrait être possible si cela est délicat pour Java avec windbg, mais si vous le faites avec .Net, la plupart des problèmes seraient les mêmes.
Je n'aime pas écrire en C si je n'ai pas besoin de le faire, mais je pense que bon nombre des affirmations faites dans ces réponses selon lesquelles la vitesse des langues autres que le C peut être écartée simplement en désassemblant le même programme en C dans votre langue de choix, en particulier si de nombreuses données sont utilisées, comme cela est courant dans les applications critiques en termes de performances. Fortran peut être une exception dans son domaine d'expertise, je ne sais pas. Est-ce un niveau supérieur à C?
La première fois que j'ai comparé le code JITed avec le code natif, nous avons résolu toutes les questions de savoir si le code .Net pouvait être exécuté de manière comparable au code C. Le niveau supplémentaire d'abstraction et toutes les vérifications de sécurité ont un coût important. Les mêmes coûts s’appliqueraient probablement à Java, mais ne vous fiez pas à Word, essayez-le sur quelque chose où les performances sont essentielles. (Quelqu'un en sait-il assez sur JITed Java pour localiser une procédure compilée en mémoire? Cela devrait certainement être possible)
J'ai trouvé une réponse sur le lien expliquant pourquoi certaines langues sont plus rapides et d'autres plus lentes. J'espère que cela clarifiera davantage la raison pour laquelle C ou C++ est plus rapide que d'autres. Il existe d'autres langues également plus rapides que C, mais nous ne pouvons utilisez-les tous. Quelques explications -
L'une des principales raisons pour lesquelles Fortran reste important est sa rapidité: les routines de calcul de nombres écrites en Fortran ont tendance à être plus rapides que les routines équivalentes écrites dans la plupart des autres langues. Les langages en concurrence avec Fortran dans cet espace, le C et le C++, sont utilisés parce qu'ils concurrencent cette performance.
Cela soulève la question: pourquoi? Qu'est-ce qui rend C++ et Fortran rapides, et pourquoi sont-ils plus performants que d'autres langages populaires, tels que Java ou Python?
Interprétation contre compilation Il existe de nombreuses façons de classer et de définir les langages de programmation en fonction du style de programmation qu’ils encouragent et des fonctionnalités qu’ils offrent. En ce qui concerne les performances, la distinction la plus importante concerne les langages interprétés et compilés.
La fracture n'est pas difficile; il y a plutôt un spectre. À une extrémité, nous avons les langages compilés traditionnels, un groupe qui comprend Fortran, C et C++. Dans ces langages, il existe une étape de compilation discrète qui traduit le code source d'un programme sous une forme exécutable que le processeur peut utiliser.
Ce processus de compilation comporte plusieurs étapes. Le code source est analysé et analysé. Les erreurs de codage de base telles que les fautes de frappe et les fautes d'orthographe peuvent être détectées à ce stade. Le code analysé est utilisé pour générer une représentation en mémoire, qui peut également être utilisé pour détecter des erreurs - cette fois, des erreurs sémantiques, telles que l'appel de fonctions inexistantes ou la tentative d'opérations arithmétiques sur des chaînes de texte.
Cette représentation en mémoire est ensuite utilisée pour piloter un générateur de code, la partie qui produit le code exécutable. L'optimisation du code, afin d'améliorer les performances du code généré, est effectuée à différents moments de ce processus: des optimisations de haut niveau peuvent être effectuées sur la représentation du code et des optimisations de niveau inférieur sont utilisées sur la sortie du générateur de code.
En réalité, l'exécution du code a lieu plus tard. L'ensemble du processus de compilation est simplement utilisé pour créer quelque chose qui peut être exécuté.
À l'opposé, nous avons des interprètes. Les interprètes incluront une étape d'analyse similaire à celle du compilateur, mais celle-ci est ensuite utilisée pour piloter l'exécution directe, le programme étant exécuté immédiatement.
L’interprète le plus simple contient un code exécutable correspondant aux diverses fonctionnalités prises en charge par le langage. Il comporte donc des fonctions permettant d’ajouter des nombres, de joindre des chaînes, quel que soit le contenu d’un langage donné. En analysant le code, il recherchera la fonction correspondante et l'exécutera. Les variables créées dans le programme seront conservées dans une sorte de table de correspondance qui mappera leurs noms sur leurs données.
L'exemple le plus extrême du style d'interprète est quelque chose comme un fichier de commandes ou un script Shell. Dans ces langages, le code exécutable n'est souvent même pas intégré à l'interpréteur, mais plutôt à des programmes distincts et autonomes.
Alors, pourquoi cela fait-il une différence en termes de performances? En général, chaque couche d'indirection réduit les performances. Par exemple, le moyen le plus rapide d’ajouter deux nombres est d’avoir ces deux nombres dans des registres du processeur et d’utiliser l’instruction d’ajout du processeur. C'est ce que peuvent faire les programmes compilés. ils peuvent mettre des variables dans des registres et tirer parti des instructions du processeur. Mais dans les programmes interprétés, cette même addition peut nécessiter deux recherches dans une table de variables pour extraire les valeurs à ajouter, puis appeler une fonction pour effectuer cette addition. Cette fonction peut très bien utiliser la même instruction de processeur que le programme compilé utilise pour effectuer l'addition réelle, mais tout le travail supplémentaire requis avant que l'instruction puisse réellement être utilisée ralentit les choses.
Si vous voulez en savoir plus s'il vous plaît vérifier la Source
C'est la différence entre automatique et manuel, les langages de haut niveau sont des abstractions donc automatisées. C/C++ sont contrôlés et gérés manuellement, même le code de vérification d'erreur est parfois un travail manuel.
C et C++ sont également des langages compilés, ce qui signifie qu'aucune de ces opérations ne doit être exécutée partout dans le monde. Ces langages doivent être adaptés au matériel avec lequel vous travaillez, ce qui ajoute une couche supplémentaire de sécurité. Bien que cela devienne un peu flou, les compilateurs C/C++ sont de plus en plus répandus sur toutes les plateformes. Vous pouvez faire des compilations croisées entre plates-formes. Ce n’est toujours pas une situation d’exécution générale, votre compilateur A ayant pour instruction de compiler contre un compilateur B de même architecture de code différent.
En bout de ligne, les langages C ne sont pas censés être faciles à comprendre ni à raisonner, c'est aussi pourquoi ils sont appelés langages systèmes. Ils sont sortis avant tout ce non-sens d'abstraction de haut niveau. C'est aussi pourquoi ils ne sont pas utilisés pour la programmation Web front-end. Ils ne sont tout simplement pas adaptés à la tâche, ils ont la capacité de résoudre des problèmes complexes qui ne peuvent pas être résolus avec un outil de langage conventionnel.
C'est pourquoi vous obtenez des choses folles comme (micro-architectures, pilotes, physique quantique, jeux AAA, systèmes d'exploitation), il y a des choses que C et C++ conviennent parfaitement. La rapidité et le calcul étant le principal domaine.
Certains algorithmes C++ sont plus rapides que C et certaines implémentations d'algorithmes ou de modèles de conception dans d'autres langages peuvent être plus rapides que C.
Lorsque les gens disent que le langage C est rapide, puis qu'ils parlent d'un autre langage, ils utilisent généralement les performances du C comme point de repère.
Laissant de côté les techniques d'optimisation avancées telles que optimisation de point chaud _, _ { méta-algorithmes pré-compilés , et diverses formes de parallélisme , la vitesse fondamentale d'un langage est corrélée. fortement avec la complexité implicite en coulisse nécessaire pour prendre en charge les opérations qui seraient généralement spécifiées dans boucles internes .
La plus évidente est peut-être la vérification de la validité des références indirectes en mémoire, telle que la vérification des pointeurs pour null
et la vérification des index par rapport aux limites du tableau. La plupart des langages de haut niveau effectuent ces contrôles de manière implicite, mais pas le C. Cependant, ceci n'est pas nécessairement une limitation fondamentale de ces autres langages - un compilateur suffisamment intelligent peut éventuellement supprimer ces vérifications des boucles internes d'un algorithme via une forme de mouvement de code invariant par une boucle .
L'avantage le plus fondamental de C (et dans une mesure similaire, du C++ étroitement lié) est une forte dépendance sur allocation de mémoire basée sur une pile , qui est intrinsèquement rapide pour l'allocation, la désallocation et l'accès. En C (et C++), la pile d'appels primaire peut être utilisée pour l'allocation de primitives, de tableaux et d'agrégats (struct
/class
).
Bien que C offre la capacité de allouer dynamiquement mémoire de taille et de durée de vie arbitraires (en utilisant le "tas"), cela est évité par défaut (la pile est utilisée à la place).
De manière fastidieuse, il est parfois possible de répliquer la stratégie d’allocation de mémoire C dans les environnements d’exécution d’autres langages de programmation. Cela a été démontré par asm.js , qui permet de traduire le code écrit en C ou C++ en un sous-ensemble de JavaScript et de l'exécuter en toute sécurité dans un environnement de navigateur Web, à une vitesse presque native.
De plus, la possibilité de s’intégrer de manière transparente aux jeux d’instructions machine natifs est un autre domaine dans lequel C et C++ surpassent la plupart des autres langages. Un exemple notable de ceci est la disponibilité (dépendante de la plate-forme et du compilateur) de intrinsèques SIMD qui prend en charge la construction d'algorithmes personnalisés qui tirent parti du matériel de traitement parallèle presque omniprésent, tout en utilisant les abstractions d'allocation de données. fourni par le langage (l’allocation des registres de niveau inférieur est gérée par le compilateur).
1) Comme d’autres l’ont dit, C fait moins pour vous. Pas de variables d'initialisation, pas de vérification des limites du tableau, pas de gestion de la mémoire, etc. Ces fonctionnalités dans d'autres langues coûtent des cycles de mémoire et de CPU que C ne dépense pas.
2) Les réponses disant que C est moins abstraite et donc plus rapide ne sont qu'à moitié correctes je pense. Techniquement parlant, si vous aviez un "compilateur suffisamment avancé" pour le langage X, celui-ci pourrait s'approcher de la vitesse de C. directement au langage d'assemblage que même un compilateur naïf peut faire un travail décent. Pour quelque chose comme Python, vous avez besoin d’un compilateur très avancé pour prédire les types d’objets probables et générer du code machine à la volée - la sémantique de C est assez simple pour qu’un compilateur puisse bien faire.
Avec les compilateurs d’optimisation modernes, il est très improbable qu’un programme en langage C pur soit aussi rapide que le code .net compilé, voire pas du tout. Grâce à l'amélioration de la productivité fournie aux développeurs par des frameworks tels que .net, vous pouvez effectuer des tâches journalières qui duraient des semaines, voire des mois, en temps normal. Couplé au coût peu élevé du matériel par rapport au salaire d'un développeur, il n'y a que moins cher d'écrire le contenu dans un langage de haut niveau et de lancer du matériel à n'importe quelle lenteur.
La raison pour laquelle Jeff et Joel parlent de C comme étant le "vrai programmeur", c'est parce qu'il n'y a pas de main-courante en C. Vous devez allouer votre propre mémoire, libérer cette mémoire, faire votre propre vérification des limites, etc. Il n'y a rien de tel. comme nouvel objet (); Il n'y a pas de garbage collection, de classes, de POO, de structures d'entités, de LINQ, de propriétés, d'attributs, de champs ou quoi que ce soit du genre. Vous devez savoir des choses comme l'arithmétique de pointeur et savoir déréférencer un pointeur. Et, d'ailleurs, savoir et comprendre ce qu'est un pointeur. Vous devez savoir ce qu'est un cadre de pile et quel est le pointeur d'instruction. Vous devez connaître le modèle de mémoire de l'architecture de processeur sur laquelle vous travaillez. Il y a beaucoup de compréhension implicite de l'architecture d'un micro-ordinateur (généralement le micro-ordinateur sur lequel vous travaillez) lors de la programmation en C qui n'est tout simplement pas présente ni nécessaire lors de la programmation dans quelque chose comme C # ou Java. Toutes ces informations ont été transférées au programmeur du compilateur (ou de la machine virtuelle).
En fait, dans certaines applications (numériques), même C peut être battu, et je ne parle pas du langage d'assemblage, mais du vieux Fortran souvent ridiculisé. La raison en est que Fortran ne garantit aucun alias de pointeur.
C est rapide parce que c’est un langage bas niveau, compilé nativement. Mais C n'est pas le plus rapide. Le Indice de Fibonacci récursif montre que Rust, Crystal et Nim peuvent être plus rapides.
Parcourez simplement le code machine de votre IDE et vous verrez pourquoi c'est plus rapide (si c'est plus rapide). Il laisse beaucoup de main dans la main. Il est également possible de demander à votre Cxx de le laisser de côté, auquel cas il devrait en être de même.
Les optimisations du compilateur sont surestimées, de même que presque toutes les perceptions concernant la vitesse de la langue.
L'optimisation du code généré fait uniquement une différence dans le code de hotspot, c'est-à-dire des algorithmes serrés sans appels de fonction (explicites ou implicites). Partout ailleurs, les résultats sont minimes.
Tout est une question de temps et d'effort.
Compte tenu d'une quantité infinie de temps et d'efforts:
Compte tenu du temps et des efforts impartis:
Pourquoi? En effet, plus vous faites d'abstraction, plus vous pouvez consacrer de temps à l'optimisation des sections de code critiques qui comptent vraiment. Voici quelques hypothèses: un développeur est également compétent dans les trois langues, vous ne vous souciez pas de la taille binaire, de l'utilisation de la mémoire, etc.
Chaque abstraction a un rapport qualité-prix mais devrait rendre le code plus facile et plus rapide à écrire.
Même la différence entre C et C++ peut parfois être grande.
Lorsque vous allouez de la mémoire à un objet, appelez des constructeurs, alignez la mémoire sur des limites de Word, etc., le programme finit par subir de nombreuses pertes qui sont extraites du programmeur.
C vous oblige à examiner chaque chose que fait votre programme, généralement avec un niveau de détail très fin. Cela rend plus difficile (bien que ce ne soit aucunement impossible) l’écriture de code faisant beaucoup de tâches inutiles pour l’objectif immédiat.
Ainsi, dans un programme BASIC, par exemple, vous utiliseriez le mot clé INPUT pour lire une chaîne sous la forme STDIN et allouer automatiquement de la mémoire pour sa variable./O ou non, et s'il arrête de lire l'entrée après avoir reçu l'information dont il a besoin ou continue à lire des caractères jusqu'à la fin de la ligne.
C effectue aussi beaucoup moins de vérification d’erreur que les autres langages, en supposant que le programmeur sache ce qu’il fait. Donc, alors que dans PHP si vous déclarez une chaîne $myStr = getInput();
et passez à la référence $myStr[20]
, mais que l'entrée ne comporte que 10 caractères, PHP interceptera ceci et vous retournera une chaîne vide. C suppose que vous avez soit alloué suffisamment de mémoire pour conserver les données au-delà de la fin de la chaîne, soit que vous savez quelle information vient après la chaîne et que vous essayez de la référencer à la place. Ces petits facteurs ont un impact énorme sur les frais généraux globalement.