Quand j'étais petit, la programmation était simple. Mon ami avait un ordinateur et il y avait Basic et Assembly. Vous pouvez écrire votre programme en Basic, ce qui est plus facile à faire mais votre programme sera lent, ou vous pouvez écrire quelque chose en Assembly, ce qui est plus difficile, mais votre programme s'exécutera beaucoup plus rapidement.
L'explication était simple aussi. Basic était un interpréteur, afin d'exécuter votre programme, il devait parcourir votre code chaque fois que vous l'invoquez et l'interpréter ligne par ligne. S'il indique "PRINT X", l'interpréteur devait trouver une variable nommée "X", trouver une routine qui effectue l'impression et appeler la routine trouvée pour la variable trouvée.
L'assemblage était, eh bien, l'assemblage. Il a également interprété votre programme d'une certaine manière, mais une seule fois lorsque vous exécutez l'assembleur. Après cela, votre programme serait exécutable sans interprétation. Les programmes nécessitant une interprétation s'exécutent plus lentement que les programmes qui n'en ont pas besoin. Si, bien sûr, ce sont des programmes équivalents.
Et dans Basic et Assembly, ils l'étaient généralement. Basic est un langage impératif, même pas trop convivial pour la programmation structurelle. Même une chose aussi basique qu'une "fonction" n'est pas une construction de langage intégrée mais un modèle : "GOSUB ... RETURN", à peu près comme "call ... ret" dans Assembly.
Avance rapide de 30 ans. Les langues sont nombreuses. Les ordinateurs sont partout. La programmation n'est plus simple. Mon département gagne son pain en réécrivant le code du chercheur écrit à l'origine en Python en C++ pour des raisons de performance, car il est de notoriété publique que Python est interprété et lent, et que C++ est compilé et rapide. Mais d'une manière ou d'une autre, chaque année, cette réécriture devient de plus en plus difficile à gagner. Quelque chose change et change vite. La connaissance commune ne change cependant pas, nous continuons donc à réécrire.
Mais maintenant, nous sommes obligés de tout optimiser comme des fous juste pour justifier ce que nous faisons. Un algorithme arrive en Python, nous le réécrivons en C++ de manière équivalente, et du coup il devient 3 fois plus lent. Ce n'est... pas pour ça qu'on est là. Nous avons donc repensé l'algorithme pour pousser et obtenir l'amélioration des performances que nous avions promise. Et la plupart du temps, puisque les chercheurs ne se soucient pas du tout des performances et qu'en termes d'algorithmes, ils laissent derrière eux des fruits à portée de main, cela fonctionne.
Pourtant, toute cette affaire ressemble maintenant à une arnaque. Nous rendons le code plus lent en le réécrivant en C++ juste pour pouvoir le rendre plus rapide en réorganisant le code. Pourquoi ne pas le reconcevoir directement en Python alors ? Ah ! Le truc, c'est que nous ne connaissons pas Python. Nous connaissons un peu Python, assez pour le lire et le comprendre, mais pas assez pour y faire des programmes ultra-rapides.
Alors qu'y a-t-il à savoir?
La plupart des bibliothèques Python sont écrites en C ou Fortran. Le noyau NumPy est écrit en C ; Pandas - en Cython et C ; SciPy - en Fortran, C et partiellement C++. Ils n'ont aucune raison d'être plus lents que tout ce qui a été écrit en C++, Rust ou Julia. Ils peuvent cependant être plus rapides.
Dans notre entreprise, nous proposons à la fois des services cloud et des applications de bureau. Et les utilisateurs de bureau se fâchent lorsque la nouvelle version de leur application préférée cesse de fonctionner sur leur matériel sans aucune raison apparente. Nous gardons donc notre cible de builds de bureau ancienne. Vraiment vieux, comme le vieux pré-Nehalem. De cette façon, personne ne se fâche mais personne ne profite non plus de SSE3.
Bien sûr, une bibliothèque informatique construite pour une cible appropriée sera généralement plus rapide qu'une bibliothèque équivalente construite pour un ordinateur générique de 15 ans avec des capacités superscalaires limitées.
La bonne nouvelle est que si vous construisez pour un cloud, vous pouvez définir votre plate-forme de construction cible pour qu'elle corresponde exactement à la machine que vous achetez, puis vos bibliothèques C++ fonctionneront à la même vitesse que celles de Python et peut-être même un peu plus rapidement.
Pour être honnête, tout l'argument sur la langue la plus rapide est ridicule. Un langage n'est pas un compilateur ou un interpréteur, c'est ce qu'il est - un langage : un ensemble de règles qui spécifient comment nous devons dire à un ordinateur ce que nous voulons faire. Un langage n'est qu'un ensemble de règles, une spécification. Et rien d'autre.
La distinction même entre interprétation et compilation date du siècle dernier. De nos jours, il existe des interpréteurs C comme IGCC , PicoC ou CCons , et il existe des compilateurs Python. Les compilateurs JIT tels que [PyPy] et les compilateurs classiques de compilation avant l'exécution tels que Codon (qui a également la capacité JIT si vous ne voulez qu'une partie de votre code soit compilée).
Codon est construit sur LLVM, la même infrastructure sur laquelle Rust, Julia ou Clang sont construits. Le code construit avec Codon s'exécute, plus ou moins, aux mêmes niveaux de performance que ceux construits avec l'un d'entre eux. Il peut y avoir des inconvénients de performances en raison de la récupération de place de Python ou de grands types de données natifs, mais nous ne parlons plus de 100x ou 10x. LLVM fait sa magie. il transforme le code Python en code machine pour vous.
Il existe également des mythes sur la compilation juste-à-temps ou JIT. Certains disent qu'elle est supérieure à la technique habituelle de compilation avant l'exécution car elle compile toujours pour l'architecture dont disposent les utilisateurs, et l'exploite ainsi de manière optimale. Certains disent qu'il y a encore des frais généraux de compilation qui, avec la compilation juste-à-temps, incombent également aux utilisateurs. Cela ralentit l'exécution des programmes car ils doivent à la fois s'exécuter et se compiler en même temps.
Le problème avec les deux mythes est qu'ils sont à la fois vrais et inutiles. Oui, JIT se compile généralement en un meilleur code machine, sauf si vous créez explicitement vos binaires pour la machine cible, ce qui, soit dit en passant, se produit assez régulièrement lorsque vous vous déployez dans un cloud. Et oui, il y a une pénalité de compilation dans le runtime, mais elle est négligeable si votre runtime est mesuré en mois, ce qui, encore une fois, lorsque vous déployez dans un cloud, n'est pas quelque chose d'inouï.
Il y a donc du pour et du contre. Ce qui est important, Python (Codon en particulier) prend en charge les modes compile-before-you-run et JIT afin que vous puissiez choisir ce qui répondra le mieux à vos besoins. Les compilateurs traditionnels, tels que Clang, n'ont pas l'option JIT.
En parlant de JIT, Numba est probablement la technologie la plus révolutionnaire dans le monde de la programmation Python ultra-rapide. C'est un compilateur mais il ne cible que les noyaux sélectionnés, pas l'ensemble du programme. Vous pouvez bien sûr sélectionner ce qui doit être compilé et pour quelle plate-forme. Dans cette configuration, vous pouvez exécuter des morceaux de votre code sur le CPU, et d'autres - sur GPGPU .
Techniquement, on peut créer des backends pour d'autres appareils spécialisés tels que le TPU de Google ou même l' accélérateur photonique de Lightmatters. Il n'y a pas encore de backend de ce type, ces gars ont décidé de déployer leur propre bibliothèque à la place. Mais, ce qui est symptomatique, ils choisissent également de fournir l'interface pour l'ordinateur photonique en Python afin que vous puissiez interagir avec Pytorch, Tensorflow ou ONNX de manière transparente.
Donc Lightmatter n'est pas encore là. Mais NVidia l'est. Ils ont fourni leur backend CUDA pour Numba et vous pouvez désormais écrire des noyaux en Python et les exécuter sur du matériel NVidia avec une efficacité maximale. C++ n'a pas ça. Il existe cependant un dialecte CU, venant de NVidia , bien sûr, qui étend C++ pour cette question précise. En Python, vous n'avez pas besoin d'étendre le langage lui-même. Étant donné que Numba fonctionne comme un compilateur de noyau JIT, l'ajout d'un backend consiste simplement à patcher une bibliothèque.
Ainsi, le modèle du noyau cible l'informatique hétérogène. Vous pouvez exécuter des morceaux de code sur différents appareils, ce qui est bien en soi. Mais il existe une autre dimension d'hétérogénéité à laquelle vous n'auriez peut-être pas pensé. Avec le modèle de noyau, vous pouvez cibler différents noyaux pour différents contextes de calcul et pas nécessairement des périphériques matériels. Cela signifie que si vous voulez qu'un noyau soit rapide mais pas particulièrement précis, vous pouvez le construire avec une option "-fast-math". Mais si, dans un autre contexte, vous voulez que ce noyau soit précis plutôt que rapide, vous pouvez reconstruire le même code sans compromis.
C'est quelque chose qui est difficile à réaliser avec les compilateurs traditionnels où vous ne pouvez pas modifier les options de compilation au milieu d'une unité de traduction. Eh bien, avec le modèle du noyau, chaque noyau est sa propre unité de traduction.
Python n'est pas lent. Ni c'est rapide. C'est juste un langage, un ensemble de règles et de mots-clés. Mais il y a beaucoup de gens qui se sont habitués à ces règles et à ces mots clés. Ils se sentent à l'aise pour écrire en Python et souhaitent améliorer Python pour eux.
Cette base d'utilisateurs est suffisamment large pour attirer à la fois de nouvelles startups dotées de technologies révolutionnaires telles que Lightmatter et leurs ordinateurs photoniques, et des entreprises bien établies avec des décennies d'expertise dans le calcul haute performance telles que NVidia. Toutes ces personnes sont largement investies pour faire de Python un meilleur... pas un langage, bien sûr, mais un environnement. L'environnement dans lequel écrire des programmes ultra-rapides est légèrement plus difficile que d'écrire des programmes lents.
Ensemble, ils font d'énormes progrès. Python devient plus rapide chaque année. À ce stade, oubliez les ordinateurs photoniques si vous le pouvez, les programmes écrits en Python fonctionnent souvent de la même manière que ceux écrits en Julia, C++ ou Rust. Mais Python ne s'arrêterait pas là. Il devient plus rapide que les compilateurs traditionnels ne deviennent plus conviviaux.
Préparez-vous à voir que dans quelques années les compilateurs Python : PyPy, Numba, ou quelque chose de complètement nouveau, emprunteront des techniques à Spiral ou Herbie pour générer du code tellement plus efficacement qu'aucun compilateur traditionnel ne pourrait s'en approcher. Après tout, écrire un nouveau backend JIT en Python est bien plus facile que de réimaginer toute l'infrastructure LLVM.