Quelles caractéristiques sémantiques de Python (et d'autres langages dynamiques) contribuent à sa lenteur?

26

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)

Basile Starynkevitch
la source
1
Lua Performance Tips , qui explique pourquoi certains programmes Lua sont lents et comment les corriger.
Robert Harvey

Réponses:

24

Quelles caractéristiques sémantiques de Python (et d'autres langages dynamiques) contribuent à sa lenteur?

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».

Jörg W Mittag
la source
Avez-vous un lien ou une référence pour Azul?
Basile Starynkevitch
4
Je ne suis pas d'accord pour dire que la sémantique d'une langue n'a aucun effet sur sa capacité à être mise en œuvre efficacement. Oui, une bonne mise en œuvre JIT avec les premières optimisations de la classe et l' analyse peut faire une grande amélioration de la performance d'une langue, mais à la fin il y a certains aspects de la sémantique qui finissent inévitablement des goulots d' étranglement étant. Que ce soit l'exigence de C pour un aliasing strict des pointeurs ou l'exigence de Python que les opérations de liste soient effectuées de manière atomique, certaines décisions sémantiques finissent inévitablement par nuire aux performances de certaines applications.
Jules
1
En passant ... avez-vous une référence pour cette amélioration de 30% pour Singularity? Je défends les systèmes d'exploitation de protection basés sur la langue depuis de nombreuses années, mais je n'ai jamais vu ce chiffre auparavant, et je le trouve assez surprenant (les chiffres que j'ai examinés dans le passé étaient plus proches de 10%) et je me demande ce que ils l'ont fait pour obtenir autant d'amélioration ...
Jules
5
@MasonWheeler: Parce qu'il n'y a que des implémentations Python merdiques. Aucun implémenteur Python n'a dépensé même une infime fraction de l'argent, des personnes, de la recherche et des ressources qu'IBM, Sun, Oracle, Google et Co. ont dépensé pour J9, JRockit, HotSpot et Co. Les 5 implémentations Python combinées ne le font probablement même pas avoir la main-d'œuvre qu'Oracle dépense uniquement pour le ramasse-miettes. IBM travaille sur une implémentation Python basée sur Eclipse OMR (le framework VM open source à composants extrait de J9), je suis prêt à parier que ses performances seront bien dans un ordre de grandeur de J9
Jörg W Mittag
2
Pour mémoire, C est lent par rapport à Fortran pour le travail numérique, car Fortran applique un alias strict afin que l'optimiseur puisse être plus agressif.
Michael Shopsin
8

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 evalfonction, 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'il evalest 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.

Jules
la source
3
Ce dernier point peut être atténué en evaldéclenchant une recompilation et / ou une désoptimisation.
Jörg W Mittag
4
Soit dit en passant, ce n'est pas unique à Python non plus. Java (ou plutôt la JVM) a un chargement de code dynamique et une liaison dynamique, donc l'analyse de la hiérarchie des classes est équivalente à la résolution du problème d'arrêt là aussi. Pourtant, HotSpot intègre heureusement les méthodes polymorphes de manière spéculative, et si quelque chose dans la hiérarchie des classes change, eh bien, il les supprimera simplement.
Jörg W Mittag