IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

La compilation à la volée (just-in-time) ne serait pas ergonomique, selon Abe Winter
Qui propose des améliorations

Le , par Stéphane le calme

369PARTAGES

14  0 
Les ordinateurs n'exécutent pas le code source que nous écrivons dans les langages de programmation, ils exécutent le code machine. Dans des circonstances normales, il existe deux façons de passer d'un langage de programmation au code machine :
  • les compilateurs, qui créent une fois un exécutable en code machine à partir de votre code source, et vous pouvez exécuter ce blob encore et encore. Compilation lente, exécution rapide
  • les interpréteurs, qui lisent le programme ligne par ligne lors de son exécution et exécutent un autre programme qui exécute ces lignes. Démarrage plus lent, performances plus lentes, mais aucune étape de compilation lente après les modifications.

Les langages compilés sont plus rapides et plus sûrs. Les langages interprétés sont plus flexibles, plus productifs et plus faciles à apprendre.

La compilation à la volée (just-in-time compilation ou JIT compilation en anglais) est une technique visant à améliorer la performance de systèmes compilés en bytecode par la traduction de bytecode en code machine natif au moment de l'exécution. La compilation à la volée se fonde sur deux anciennes idées : la compilation de bytecode et la compilation dynamique. Elle apporte donc le meilleur des deux mondes, en théorie.

La compilation à la volée s'adapte dynamiquement à la charge de travail courante du logiciel, en compilant le code « chaud », c'est-à-dire le code le plus utilisé à un moment donné (ce qui peut représenter tout le programme, mais souvent seules certaines parties du programme sont traitées par compilation à la volée). Obtenir du code machine optimisé se fait beaucoup plus rapidement depuis du bytecode que depuis du code source. Comme le bytecode déployé est portable, la compilation à la volée est envisageable pour tout type d'architecture, à la condition d'avoir un compilateur JIT pour cette architecture, ce qui est facilité par le fait que les compilateurs de bytecode en code machine sont plus faciles à écrire que les compilateurs code source - code natif.

La compilation à la volée ne serait pas ergonomique selon un développeur

Abe Winter est un développeur reconverti qui a des années d'expérience professionnelle en tant que programmeur avec différents paradigmes de langage (statique, dynamique, JIT). Il pense que JIT n’est pas ergonomique :

« Le travail sur les performances en 2020 peut concerner autant la mise en cache et la conception intelligente de RPC que les performances linéaires d'une fonction. Mais les performances en ligne droite comptent toujours. Et l'ergonomie de mesure de la performance d'un morceau de code en ligne droite en prod est mauvaise.

« J’accuse le JIT.

« J'aime l'idée de JIT, mais l'outillage est sous-développé. Le battage médiatique dit "compétitif avec C pour les fonctions numériques courtes", mais la réalité est "quelque part entre Python et Java". C’est comme un bus qui vous dépose à mi-chemin et ne vous laisse pas emporter de vélo. Il serait plus rapide de parcourir tout le chemin à vélo plutôt que d’en parcourir la moitié à pied ».


Voici les arguments qu’il avance pour soutenir son affirmation :

JS JIT rend le test de performance impossible

« Je ne sais pas comment faire ces choses en JavaScript :
  • savoir si une fonction est optimisée dans une exécution donnée ;
  • savoir si une fonction sera optimisée en prod ;
  • exiger statiquement qu'une fonction soit optimisée en prod ;
  • être informé des dépôts dans les chemins critiques (hot paths).

« En l'absence d'un sous-ensemble de ces astuces, je n'ai aucun moyen de tester à la perfection mon code JS sans tester la charge à pleine échelle. Cela finit par être une énorme perte de temps et c'est la raison pour laquelle les gens réécrivent les services JS dans d'autres langages lorsqu'ils grandissent. »

Les benchmarks mentent

« C'est peut-être une façon forte de le dire. Mais les langages JIT rendent difficile l'utilisation du micro-benchmarking pour prédire les performances du même morceau de code en prod, pour plusieurs raisons:
  • les benchmarks (qui exécutent la même fonction avec les mêmes entrées des millions de fois de suite) sont très amies avec la gigue (jitter en anglais, variation de la latence au fil du temps) ;
  • la fonction que vous testez peut être appelée avec différentes entrées en prod. Dans JS, même l'ordre des clés dans un objet peut confondre le typer JIT ;
  • si vous comparez une fonction JIT / native et que votre configuration de produit finit par utiliser du code interprété, vous n'aurez rien appris.


« Ne pas savoir combien de temps prend la réalité mène au vaudou et à la gymnastique.

« Oui, les langages compilés ont leur propre version de ceci avec une cohérence de cache. Oui, le JIT peut conduire à de meilleures performances que la précompilation en théorie à cause du collecteur de statistiques, mais je ne contrôle aucune de ces choses. »

Il est plus facile d'écrire du code performant (choisissez votre poison)

« Et plus important encore, maintenir le code performant. Ne vous méprenez pas, je ne dis pas que C++, Go ou Java sont des langages plus productifs que JS. Mais si votre budget est tel que les serveurs coûtent plus cher que la paie, il est difficile de conserver votre logique de base à grande échelle dans un langage interprété / JIT.

Pour deux raisons :
  • l’optimisation des hot paths : il y aura un moment où vous aurez besoin d'un chemin de code plus rapide et le JIT fera que cela marche comme si vous étiez en train de vous servir de la gélatine avec des pincettes ;
  • des régressions de performances : quelqu'un va à un moment donné effectuer un changement moyen ou grand dans le code qui va dégrader subtilement les performances et nuire à la qualité du produit, et à moins que votre surveillance soit excellente, vous ne le saurez pas


Abe Winter ne demande pas d’abandonner le système JIT, mais de l’améliorer

« Tout ce que je demande, c'est des informations et un contrôle. La prémisse de base de JIT de compiler uniquement des parties de votre programme me convient. Je voudrais pouvoir contrôler les parties en questions et le moment de compilation.

« Je voudrais une collecte de statistiques efficace sur les dépôts en prod, donc je sais quelles fonctions traiter comme des hot paths.

« Les langages compilés ont un paquet d'astuces qui sont en quelque sorte sur ce sujet : optimisation guidée statistiquement, optimisation de programme entier. Les compilations à la volée y arriveront. »

Voici une liste d’éléments qu’il voudrait voir implémentés :


Source : Abe Winter

Et vous ?

Partagez-vous son avis ? Dans quelle mesure ?
Que pensez-vous des arguments qu'il a avancés ?
En avez-vous quelques-un pour soutenir ou relever les limites de son affirmation ?
Que pensez-vous des propositions qu'il a faites ? En avez-vous d'autres ?

Une erreur dans cette actualité ? Signalez-le nous !

Avatar de abriotde
Membre chevronné https://www.developpez.com
Le 02/04/2020 à 8:51
Est-ce donc vraiment une critique de JIT globale
Oui on parle bien de JIT, javascript est pris en exemple.

Les arguments avancés sont tout a fais justifiés. Il ne dis pas que JIT est inutile il dis juste que dans du code réel (ou il n'y a pas une grande majorité de boucle répétés un grand nombre de fois mais plutôt beaucoup de branchement conditionnel et petites boucle), certes JIT accélère les grande boucle mais il empêche de traiter aussi efficacement qu'on l'aurait fais sans JIT les autres parties du code. Et il explique aussi que c'est très difficile a analyser.

En ce sens il a raison. Il ne faut pas le voir comme un refus du JIT, mais comme une critique constructive pour focaliser la recherche autour du JIT plutôt que sur le JIT.
5  0 
Avatar de gangsoleil
Modérateur https://www.developpez.com
Le 08/02/2021 à 17:30
Hello,
Citation Envoyé par StringBuilder Voir le message
Si JavaScript n'est pas optimisable à cause du JIT, et que c'est le principe même du JIT qui pose problème, alors Java souffre du même problème (tout comme .NET qui, si je ne m'abuse, est le père du JIT, je trouve ça assez surprenant qu'il ne soit même pas mentionné).
JIT : smalltalk, 1983 (https://en.wikipedia.org/wiki/Just-i...lation#History)

Pour moi, tout ce que dit ce monsieur est d'une évidence navrante.
J'ai bossé un peu chez Sun Microsystems sur la JVM Java (début des années 2000, OK ça fait un bail, et non vous ne pouvez pas le vérifier), ces problématiques de JIT m'ont clairement été expliquées, et les problématiques étaient exactement les mêmes : que compiler et pourquoi ? Comment tu choisis que tu vas plutôt compiler cette fonction plutôt qu'une autre, et tu peux pousser sur les différentes optimisations de compilation, et pleins d'autres problématiques super intéressantes, mais qui n'ont rien de nouveau.

« Je voudrais une collecte de statistiques efficace sur les dépôts en prod, donc je sais quelles fonctions traiter comme des hot paths.
Mais ce que ne voit pas ce monsieur, c'est que la manière dont moi j'utilise un logiciel n'est pas la même que sa manière d'utiliser le même logiciel. Bien sûr, si on prend un hello world de 5 lignes, il n'y a qu'un seul chemin critique, donc c'est simple. Mais si je prends un navigateur, celui qui a 200 onglets d'ouvert et 15 signets n'a pas la même utilisation que celui qui a 20 000 signets mais pas plus de 3 onglets -- c'est le même logiciel, mais le choix des fonctions à compiler par le JIT ne sont clairement pas les mêmes.
4  0 
Avatar de walfrat
Membre expérimenté https://www.developpez.com
Le 02/04/2020 à 0:27
Le titre suggère une attaque sur le JIT en général, cependant les arguments donnés ne semblent concernés que le Javascript.

Est-ce donc vraiment une critique de JIT globale, ou juste de l'état de celui en JavaScript ?

Je peux comprendre la difficulté de parler de performance avec du JIT en jeu mais j'imagine que ces questions doivent avoir des réponses avec par exemple le JIT en Java depuis le temps que ça existe non ?

Enfin, les arguments tel qu'énoncé me rappelle vaguement le genre qu'on peut entendre contre le garbage collector, ou contre quelqu'un qui se plaindrait qu'un compilateur réécrit sa fonction récursive en boucle a la compilation. "Je ne contrôle pas", oui, c'est fait exprès, parce que généralement les éléments en jeu feront mieux que "toi" (toi = le développeur moyen ici), si tu as un besoin absolu de contrôle, utilise un langage qui est fait pour ça, tu peux même écrire ta propre librairie C++ et l'utiliser dans Java, pareil côté nodeJS. Je ne suis pas un expert loin de là, mais il me semble bien que c'est prévu ainsi, ce n'est pas étonnant d'avoir des soucis si on veut faire autrement que comment c'est prévu.
3  0 
Avatar de Mingolito
Membre extrêmement actif https://www.developpez.com
Le 29/01/2021 à 16:20
Faux, il était possible de faire un package code VB6 plus runtime mais cela n'à rien à voir avec la compilation native C++ ou Delphi qui eux disposent d'un vrai compilateur.

C'était juste du bullshit marketing.
3  1 
Avatar de Mingolito
Membre extrêmement actif https://www.developpez.com
Le 30/01/2021 à 14:51
Tu n'a rien prouvé du tout avec ton blabla qui mélange tout.
Des benchmarks ont été faits qui ont montré que la différence de performance entre compiler en pcode ou en natif avec VB6 est minime, genre 1% d'amélioration, parce que c'est bidon, c'est du bullshit marketing, qui a été inventé à l'époque par le marketing justement pour faire croire que c'est du natif comme C++ ou Delphi, mais c'est faux.

Créer un package .exe qui encapsule à la fois un runtime et du pcode n'à rien à voir avec de la compilation native, comme le fait un compilateur C, C++ ou Delphi.

De fait les éditeurs de logiciels font généralement des logiciels avec C++ ou parfois Delphi mais absolument pas avec VB6.

Par exemple Windev c'est pas de la compilation native, par contre Windev est développé en C++.
2  1 
Avatar de rt15
Membre éclairé https://www.developpez.com
Le 30/01/2021 à 15:05
Mon blabla ne prouve rien en effet. Tout comme le tien.

Mais le code assembleur ne ment pas. La réalité technique est que le .exe contient mon algo en code natif, pas en bytecode.

Par contre je suis entièrement d'accord depuis le début pour dire que le VB6 est un escargot paraplégique comparé à du C ou du Delphi.

[edit]

Même exercice en C, compilation du code suivant avec gcc en 32 bits sans optimisations :
Code c : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
  
int first, second, temp, i; 
  
first = 0; 
second = 1; 
for (i = 0; i < n; i++) 
{ 
  temp = first + second; 
  first = second; 
  second = temp; 
} 
return first;

Désassemblage du .exe :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
0040153f c745f400000000  mov     dword ptr [ebp-0Ch],0         first = 0 
00401546 c745f001000000  mov     dword ptr [ebp-10h],1         second = 1 
0040154d c745ec00000000  mov     dword ptr [ebp-14h],0         i = 0 
00401554 eb1b            jmp     fibonnaci+0x1571 (00401571)   jmp second_label 
 
first_label 
00401556 8b55f4          mov     edx,dword ptr [ebp-0Ch]       edx = first 
00401559 8b45f0          mov     eax,dword ptr [ebp-10h]       eax = second 
0040155c 01d0            add     eax,edx                       eax = first + second 
0040155e 8945e8          mov     dword ptr [ebp-18h],eax       temp = eax 
00401561 8b45f0          mov     eax,dword ptr [ebp-10h]       eax = second 
00401564 8945f4          mov     dword ptr [ebp-0Ch],eax       first = second 
00401567 8b45e8          mov     eax,dword ptr [ebp-18h]       eax = temp 
0040156a 8945f0          mov     dword ptr [ebp-10h],eax       second = temp 
0040156d 8345ec01        add     dword ptr [ebp-14h],1         i++ 
 
second_label 
00401571 8b45ec          mov     eax,dword ptr [ebp-14h]       eax = i 
00401574 3b4508          cmp     eax,dword ptr [ebp+8]         comparaison de i avec n 
00401577 7cdd            jl      fibonnaci+0x1556 (00401556)   jmp first_label si i < n 
00401579 8b45f4          mov     eax,dword ptr [ebp-0Ch]       Mise en place du résultat dans eax
Est ce que ce code natif est plus rapide que celui produit par le VB6 ? Oui et dans des proportions astronomiques, pas juste un facteur 2 ou 10. Le principal problème de celui de VB6 c'est l'utilisation de l'instruction call dans la boucle qui coûte un bras sans même considérer ce qui est appellé.

Mais est ce fondamentalement différent ? Non. Dans les deux cas bin c'est des instructions jmp/mov/lea sur des registres eax/edx/ebp... Du code natif x86 quoi.
Si vous désassemblez du bytecode Java ou .NET ça sera très différent au niveau des instructions et des opérandes, et du Chinois pour un processeur.

[edit 2]

Et pour le C#, ça donne quoi ?
Comme Abe Winter le remarque, c'est plus difficile de travailler avec un langage qui compile en bytecode:
  1. Il n'y a pas de code natif dans l'exécutable.
  2. Le code natif généré à l'exécution peut varier d'une exécution à l'autre et dépend bien sûr du compilo JIT et de sa version.


Mais en théorie, à l'exécution, le bytecode est compilé en code natif dans la mémoire du processus puis exécuté.
Donc avec un bon débogueur genre WinDbg et un point d'arrêt bien senti, on peut retrouver le code natif que voici (.Net Core 5) :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
00007ffe`0b69c003 33c0            xor     eax,eax                    eax = 0 
00007ffe`0b69c005 8945fc          mov     dword ptr [rbp-4],eax      first = 0 
00007ffe`0b69c008 c745f801000000  mov     dword ptr [rbp-8],1        second = 1 
00007ffe`0b69c00f 8945f0          mov     dword ptr [rbp-10h],eax    i = 0 
00007ffe`0b69c012 90              nop 
00007ffe`0b69c013 eb1f            jmp     00007ffe`0b69c034          jmp second_label 
 
first_label: 
00007ffe`0b69c015 90              nop 
00007ffe`0b69c016 8b45fc          mov     eax,dword ptr [rbp-4]      eax = first 
00007ffe`0b69c019 0345f8          add     eax,dword ptr [rbp-8]      eax = eax + second 
00007ffe`0b69c01c 8945f4          mov     dword ptr [rbp-0Ch],eax    temp = eax 
 
00007ffe`0b69c01f 8b45f8          mov     eax,dword ptr [rbp-8]      eax = second 
00007ffe`0b69c022 8945fc          mov     dword ptr [rbp-4],eax      first = eax 
 
00007ffe`0b69c025 8b45f4          mov     eax,dword ptr [rbp-0Ch]    eax = temp 
00007ffe`0b69c028 8945f8          mov     dword ptr [rbp-8],eax      second = temp 
 
00007ffe`0b69c02b 90              nop 
 
00007ffe`0b69c02c 8b45f0          mov     eax,dword ptr [rbp-10h]    eax = i 
00007ffe`0b69c02f ffc0            inc     eax                        eax++ 
00007ffe`0b69c031 8945f0          mov     dword ptr [rbp-10h],eax    i = eax 
 
second_label: 
00007ffe`0b69c034 8b45f0          mov     eax,dword ptr [rbp-10h]    eax = i 
00007ffe`0b69c037 3b4510          cmp     eax,dword ptr [rbp+10h]    comparaison entre i et n 
00007ffe`0b69c03a 0f9cc0          setl    al                         al = 1 si i < n, donc s'il faut boucler 
00007ffe`0b69c03d 0fb6c0          movzx   eax,al                     eax = al 
00007ffe`0b69c040 8945ec          mov     dword ptr [rbp-14h],eax    [rbp-14h] = eax 
00007ffe`0b69c043 837dec00        cmp     dword ptr [rbp-14h],0      comparaison de 0 avec 1 ou 0 
00007ffe`0b69c047 75cc            jne     00007ffe`0b69c015          jmp first_label si [rbp-14h] != 0 
00007ffe`0b69c049 8b45fc          mov     eax,dword ptr [rbp-4]      Mise en place du résultat dans eax
C'est donc encore une fois du code natif, mais contrairement au C et VB6, ce code est généré à l'exécution chez le client, et non à la compilation chez le développeur.
Niveau performance, il devrait être assez proche du code natif produit par le C. Il est plus fastidieux notamment pour le test de la boucle mais ce n'est pas aussi affreux que le code natif produit par VB6.
Attention cette petite comparaison ne veut pas du tout dire que le C# est presque aussi rapide que le C... Il y a plein d'autres facteurs, trop pour en parler ici.

Java au prochain épisode ?
1  0 
Avatar de Sodium
Membre extrêmement actif https://www.developpez.com
Le 01/04/2020 à 18:16
Par sûr ils veulent dire qu'un programme plante à la compilation s'il y a une erreur, un langage interprété il faut utiliser une librairie pour ça
0  0 
Avatar de FatAgnus
Membre expérimenté https://www.developpez.com
Le 02/04/2020 à 9:24
Citation Envoyé par sergio_is_back Voir le message
Un langage interprété n'est pas forcement plus "productif" ni plus facile à apprendre qu'un langage compilé (si on prend Perl par exemple... c'est parfois imbitable pour les néophytes...)
Tu confonds courbe d'apprentissage et productivité. Le langage Perl est certainement plus difficile à apprendre que le langage Python, mais une fois le langage Perl maîtrisé, un développeur Perl écrira un programme en Perl beaucoup plus rapidement que le même programme qu'en langage C.
1  1 
Avatar de moldavi
Membre extrêmement actif https://www.developpez.com
Le 10/05/2020 à 5:42
Bonjour.

Le jour où l'auteur comprendra que les OS ne sont pas temps réel, il va se faire de sacrés nœuds au cerveau, en plus du JIT.
0  0 
Avatar de StringBuilder
Expert éminent https://www.developpez.com
Le 13/05/2020 à 16:54
Citation Envoyé par abriotde Voir le message
Oui on parle bien de JIT, javascript est pris en exemple.
Je ne suis pas d'accord.

L'article ne parle QUE de JavaScript et de problématiques PUREMENT JAVASCRIPT.

Le meilleur exemple, c'est que le JIT est expliqué comme mode de fonctionnement du Java (le .class, le bytecode, etc.)

Et pourtant, il ne fait que comparer le JIT merdique de JavaScript avec... Java ! Qui tout d'un coup ne serait plus JIT, comme par enchantement !

Si JavaScript n'est pas optimisable à cause du JIT, et que c'est le principe même du JIT qui pose problème, alors Java souffre du même problème (tout comme .NET qui, si je ne m'abuse, est le père du JIT, je trouve ça assez surprenant qu'il ne soit même pas mentionné).

Enfin, je ne suis pas d'accord avec la description du JIT : faite dans l'article. Le bytecode n'est pas interprété (ce que faisait VB6 ou Java 1.4) mais bien compilé en code natif au moment du premier appel (ce que fait .NET depuis la version 1.0).
0  0