Je ne connais pas très bien Python. J'essaie de comprendre plus précisément quelles caractéristiques exactes des langages dynamiques (à la Python, Lua, Scheme, Perl, Ruby, ....) forcent leurs implémentations à être lentes.
À titre d'exemple, les machines métables de Lua 5.3 rendraient intuitivement Lua assez lent, mais dans la pratique, il est dit que Lua est assez rapide (et plus rapide que Python).
De plus, j'ai l'intuition (peut-être une mauvaise) que, puisque sur les processeurs actuels, la mémoire est beaucoup plus lente que le calcul brut (un accès à la mémoire avec un cache manquant a besoin du même temps que des centaines d'opérations arithmétiques), la vérification de type dynamique (à la if (value->type != INTEGER_TAG) return;
in C parlance) pourrait fonctionner assez rapidement.
Bien sûr, l'analyse de programme entier (comme le fait l' implémentation de Staline ) peut faire une implémentation de langage dynamique car un traducteur s'exécute rapidement, mais supposons que je n'ai pas le temps de concevoir un analyseur de programme entier au début.
(Je suis en train de concevoir un langage dynamique dans mon moniteur MELT , et certains seraient traduits en C)
la source
Réponses:
Aucun.
Les performances des implémentations de langage sont fonction de l'argent, des ressources et des thèses, et non des fonctionnalités de langage. Soi est beaucoup plus dynamique que Smalltalk et légèrement plus dynamique que Python, Ruby, ECMAScript ou Lua, et il avait une machine virtuelle qui surpassait toutes les machines virtuelles Lisp et Smalltalk existantes (en fait, la distribution Self livrée avec un petit interpréteur Smalltalk écrit en Self , et même cela était plus rapide que la plupart des machines virtuelles Smalltalk existantes), et était compétitif, et parfois même plus rapide que les implémentations C ++ de l'époque.
Ensuite, Sun a cessé de financer Self, et IBM, Microsoft, Intel et Co. ont commencé à financer C ++, et la tendance s'est inversée. Les développeurs Self ont quitté Sun pour créer leur propre entreprise, où ils ont utilisé la technologie développée pour Self VM pour construire l'une des machines virtuelles Smalltalk les plus rapides de tous les temps (la machine virtuelle animorphe), puis Sun a racheté cette entreprise et une version légèrement modifiée de que Smalltalk VM est désormais mieux connu sous le nom de "HotSpot JVM". Ironiquement, les programmeurs Java regardent les langages dynamiques comme «lents», alors qu'en fait, Javaétait lent jusqu'à ce qu'il adopte une technologie de langage dynamique. (Oui, c'est vrai: la machine virtuelle Java HotSpot est essentiellement une machine virtuelle Smalltalk. Le vérificateur de bytecode fait beaucoup de vérification de type, mais une fois que le bytecode est accepté par le vérificateur, la VM, et en particulier l'optimiseur et le JIT ne le font pas réellement beaucoup d'intérêt avec les types statiques!)
CPython ne fait tout simplement pas beaucoup de choses qui rendent les langages dynamiques (ou plutôt la répartition dynamique) rapides: compilation dynamique (JIT), optimisation dynamique, insertion spéculative, optimisation adaptative, désoptimisation dynamique, rétroaction / inférence de type dynamique. Il y a aussi le problème que presque tout le noyau et la bibliothèque standard sont écrits en C, ce qui signifie que même si vous accélérez Python 100x tout d'un coup, cela ne vous aidera pas beaucoup, car quelque chose comme 95% du code exécuté par un Le programme Python est C, pas Python. Si tout était écrit en Python, même des accélérations modérées créeraient un effet d'avalanche, où les algorithmes deviendraient plus rapides et les infrastructures de données de base deviendraient plus rapides, mais bien sûr, les structures de données de base sont également utilisées dans les algorithmes, et les algorithmes de base et les données de base les structures sont utilisées partout ailleurs,
Il y a deux ou trois choses qui sont notoirement mauvaises pour les langages OO gérés en mémoire (dynamiques ou non) dans les systèmes d'aujourd'hui. La mémoire virtuelle et la protection de la mémoire peuvent être un tueur pour les performances de récupération de place en particulier et les performances du système en général. Et cela est complètement inutile dans un langage à mémoire sûre: pourquoi se protéger contre les accès illégaux à la mémoire alors qu'il n'y a pas d'accès à la mémoire dans la langue pour commencer? Azul a découvert qu'il utilisait des MMU puissants et modernes (Intel Nehalem et plus récent, et l'équivalent d'AMD) pour aider à la collecte des ordures au lieu de le gêner, mais même s'il est pris en charge par le CPU, les sous-systèmes de mémoire actuels des systèmes d'exploitation traditionnels ne sont pas assez puissants pour permettre cela (c'est pourquoi la JVM d'Azul fonctionne réellement virtualisée sur nu plus l'OS, pas en son sein).
Dans le projet Singularity OS, Microsoft a mesuré un impact d'environ 30% sur les performances du système lors de l'utilisation de la protection MMU au lieu du système de type pour la séparation des processus.
Azul a également remarqué lors de la construction de leurs CPU Java spécialisés que les processeurs traditionnels traditionnels se concentrent sur la mauvaise chose en essayant de réduire le coût des échecs de cache: ils essaient de réduire le nombre d'erreurs de cache grâce à des choses telles que la prédiction de branche, la prélecture de la mémoire, etc. Mais, dans un programme OO fortement polymorphe, les modèles d'accès sont fondamentalement pseudo-aléatoires, il n'y a tout simplement rien à prévoir. Ainsi, tous ces transistors sont simplement gaspillés, et ce que l'on devrait faire à la place est de réduire le coût de chaque échec de cache individuel. (Le coût total est #miss * coût, le courant principal essaie de faire tomber le premier, Azul le second.) Les accélérateurs de calcul Java d'Azul pourraient avoir 20000 échecs de cache simultanés en vol et continuer à progresser.
Quand Azul a commencé, ils pensaient qu'ils prendraient quelques impromptu composants E / S et concevoir leur propre noyau CPU spécialisée, mais ce qu'ils ont réellement fini besoin de faire était exactement le contraire: ils ont pris un hors-the assez classique étagère à 3 adresses RISC et conçu leur propre contrôleur de mémoire, MMU et sous-système de cache.
tl; dr : La "lenteur" de Python n'est pas une propriété du langage mais a) sa mise en œuvre naïve (principale), et b) le fait que les CPU et OS modernes sont spécifiquement conçus pour faire tourner C rapidement, et les fonctionnalités qu'ils ont pour C n'aident pas (cache) ou même nuisent activement (mémoire virtuelle) aux performances Python.
Et vous pouvez insérer à peu près n'importe quel langage géré par la mémoire avec un polymorphisme ad hoc dynamique ici… en ce qui concerne les défis d'une implémentation efficace, même Python et Java sont à peu près «le même langage».
la source
Alors que l'implémentation actuelle de Python (qui manque beaucoup des optimisations effectuées par d'autres langages dynamiques, par exemple les implémentations Javascript modernes et, comme vous le faites remarquer, Lua) est une source de la plupart de ses problèmes, elle a quelques problèmes sémantiques qui la rendraient difficile pour une mise en œuvre de concurrencer d'autres langues, du moins dans certains domaines. Certains méritent une attention particulière:
Les opérations de liste et de dictionnaire sont requises par la définition du langage pour être atomiques. Cela signifie qu'à moins qu'un compilateur JIT soit en mesure de prouver qu'aucune référence à un objet de liste n'a échappé à son thread actuel (une analyse qui est difficile dans de nombreux cas et impossible dans le cas général), il doit s'assurer que l'accès à l'objet est sérialisé (par exemple via verrouillage). L'implémentation de CPython résout ce problème en utilisant le fameux "verrou d'interpréteur global" qui empêche le code Python d'être utilisé efficacement dans les environnements de multi-traitement avec des techniques multi-thread, et bien que d'autres solutions soient possibles, elles ont toutes des problèmes de performances.
Python n'a pas de mécanisme pour spécifier l'utilisation d'objets de valeur; tout est géré par référence, en ajoutant une indirection supplémentaire là où elle n'est pas nécessairement requise. Bien qu'il soit possible pour un compilateur JIT de déduire des objets de valeur dans certains cas et de l'optimiser automatiquement, il n'est pas possible de le faire de manière générale et donc du code qui n'est pas soigneusement écrit pour garantir l'optimisation est possible (ce qui est un peu un art noir) va souffrir.
Python a une
eval
fonction, ce qui signifie qu'un compilateur JIT ne peut pas faire d'hypothèses sur les actions qui ne se produisent pas, même s'il effectue une analyse de programme entier, tant qu'ileval
est utilisé une fois. Par exemple, un compilateur Python ne peut pas supposer qu'une classe n'a pas de sous-classes et donc dévirtualiser les appels de méthode, car cette hypothèse pourrait plus tard être annulée via un appel àeval
. Il doit plutôt effectuer des vérifications de type dynamiques pour garantir que les hypothèses émises par le code natif compilé n'ont pas été invalidées avant l'exécution de ce code.la source
eval
déclenchant une recompilation et / ou une désoptimisation.