1 points par GN⁺ 2023-12-26 | 1 commentaires | Partager sur WhatsApp

Sortie de Ruby 3.3.0

  • La version Ruby 3.3.0 est disponible. Elle introduit Prism, un nouveau parseur, utilise Lrama comme générateur de parseur, ajoute RJIT, un compilateur JIT écrit en Ruby pur, et apporte surtout des améliorations de performance à YJIT.

Parseur Prism

  • Prism est un parseur récursif descendant pour le langage Ruby, portable, robuste face aux erreurs et facile à maintenir, fourni comme gem par défaut.
  • Prism est adapté à la production, activement maintenu, et peut être utilisé en remplacement de Ripper.
  • Une documentation détaillée sur l’utilisation de Prism est fournie.
  • Prism est à la fois une bibliothèque C utilisée en interne par CRuby et un gem Ruby utilisable par tout outil ayant besoin d’analyser du code Ruby.
  • Les principales méthodes de l’API Prism incluent Prism.parse(source), Prism.parse_comments(source), Prism.parse_success?(source).
  • Il est possible de contribuer directement au dépôt Prism en soumettant des pull requests ou des issues.
  • Pour utiliser expérimentalement le compilateur Prism, on peut utiliser ruby --parser=prism ou RUBYOPT="--parser=prism", mais cela doit rester réservé au débogage.

Générateur de parseur Lrama

  • Bison est remplacé par le générateur de parseur LALR Lrama.
  • Les personnes intéressées peuvent consulter la vision future du parseur de Ruby.
  • Pour faciliter la maintenance, le parseur interne de Lrama est remplacé par un parseur LR généré par Racc.
  • Prise en charge des règles paramétrées (?, *, +), prévues pour être utilisées dans le parse.y de Ruby.

YJIT

  • D’importantes améliorations de performance sont présentes par rapport à Ruby 3.2.
  • La prise en charge des arguments splat et rest est améliorée.
  • Des registres sont alloués pour les opérations de pile de la machine virtuelle.
  • Davantage d’appels avec arguments optionnels sont compilés. Les gestionnaires d’exceptions sont également compilés.
  • Les types d’appels non pris en charge et les sites d’appel mégamorphiques ne repassent plus dans l’interpréteur.
  • Des méthodes de base comme #blank? de Rails et le #present? spécialisé sont traitées inline.
  • Integer#*, Integer#!=, String#!=, String#getbyte, Kernel#block_given?, Kernel#is_a?, Kernel#instance_of?, Module#=== sont notamment optimisées.
  • La vitesse de compilation est légèrement supérieure à celle de Ruby 3.2.
  • Sur Optcarrot, c’est plus de 3 fois plus rapide que l’interpréteur.
  • L’utilisation mémoire est nettement améliorée par rapport à Ruby 3.2.
  • Les métadonnées du code compilé utilisent bien moins de mémoire.
  • --yjit-call-threshold passe automatiquement de 30 à 120 pour les applications comportant plus de 40 000 ISEQ.
  • --yjit-cold-threshold est ajouté pour ignorer la compilation des ISEQ refroidis.
  • Un code plus compact est généré sur Arm64.
  • Le code GC est désactivé par défaut.
  • --yjit-exec-mem-size est traité comme une limite stricte au-delà de laquelle la compilation de nouveau code s’arrête.
  • Il n’y a pas de baisse de performance liée au code GC, et avec Pitchfork le serveur présente un meilleur comportement en copy-on-write lors du refork.
  • Si souhaité, le code GC peut être activé avec --yjit-code-gc.
  • RubyVM::YJIT.enable est ajouté pour activer YJIT à l’exécution.
  • Rails 7.2 prévoit d’utiliser cette méthode pour activer YJIT par défaut.
  • Cette méthode peut être utilisée pour n’activer YJIT qu’une fois le démarrage de l’application terminé.
  • Pour désactiver YJIT au démarrage tout en utilisant d’autres options YJIT, on peut utiliser --yjit-disable.
  • Davantage de statistiques YJIT sont fournies par défaut.
  • yjit_alloc_size et plusieurs statistiques liées aux métadonnées sont fournies par défaut.
  • La statistique ratio_in_yjit produite par --yjit-stats est disponible dans les builds de release. Il n’est plus nécessaire d’utiliser une build spéciale de statistiques ou de développement.
  • Davantage de fonctions de profiling sont ajoutées.
  • --yjit-perf est ajouté pour faciliter le profiling avec Linux perf.
  • --yjit-trace-exits prend en charge l’échantillonnage avec --yjit-trace-exits-sample-rate=N.
  • Des tests plus poussés et de nombreuses corrections de bugs ont été apportés.

RJIT

  • RJIT, un compilateur JIT écrit en Ruby pur, est introduit et remplace MJIT.
  • RJIT n’est pris en charge que sur l’architecture x86-64 des plateformes Unix.
  • Contrairement à MJIT, aucun compilateur C n’est nécessaire à l’exécution.
  • RJIT n’existe qu’à des fins expérimentales.
  • En production, il faut continuer à utiliser YJIT.
  • Si vous vous intéressez au développement du JIT de Ruby, il est recommandé de consulter la présentation de k0kubun au 3e jour de RubyKaigi.

Ordonnanceur de threads M:N

  • Un ordonnanceur de threads M:N est introduit.
  • M threads Ruby sont gérés par N threads natifs (threads du système d’exploitation), ce qui réduit le coût de création et de gestion des threads.
  • L’ordonnanceur de threads M:N peut casser la compatibilité avec les extensions C ; il est donc désactivé par défaut dans le Ractor principal.
  • Il est possible d’activer les threads M:N dans le Ractor principal avec la variable d’environnement RUBY_MN_THREADS=1.
  • Dans les Ractor non principaux, les threads M:N sont toujours activés.
  • La variable d’environnement RUBY_MAX_CPU=n définit le nombre maximal de N (le nombre maximal de threads natifs). La valeur par défaut est 8.
  • Un seul thread Ruby peut s’exécuter par Ractor ; ainsi, une application mono-Ractor (la plupart des applications) n’utilise qu’un seul thread natif.
  • Davantage de threads natifs que N peuvent être utilisés pour prendre en charge les opérations bloquantes.

Améliorations de performance

  • defined?(@ivar) est optimisé à l’aide d’Object Shapes.
  • La résolution de noms comme Socket.getaddrinfo peut désormais être interrompue (dans les environnements compatibles avec pthreads).
  • Plusieurs améliorations de performance concernent le garbage collector.
    • Quand un jeune objet est référencé par un objet ancien, il n’est plus immédiatement promu dans l’ancienne génération, ce qui réduit fortement la fréquence des major GC.
    • Une nouvelle variable de réglage REMEMBERED_WB_UNPROTECTED_OBJECTS_LIMIT_RATIO, qui contrôle le nombre d’objets non protégés déclenchant une major GC, est introduite. Sa valeur par défaut est 0.01 (1 %), ce qui réduit fortement la fréquence des major GC.
    • Les Write Barriers manquantes ont été implémentées pour de nombreux types cœur, ce qui réduit fortement le temps des minor GC et la fréquence des major GC.
    • La plupart des classes cœur utilisent désormais la Variable Width Allocation, ce qui accélère l’allocation et la libération, réduit l’usage mémoire et diminue la fragmentation du heap.
    • Le support des références faibles est ajouté au garbage collector.

Autres changements notables

  • IRB reçoit plusieurs améliorations, dont une intégration avancée irb:rdbg, la prise en charge d’un pager pour les commandes ls, show_source et show_cmds, une meilleure précision et utilité des informations fournies par ls et show_source, ainsi qu’une autocomplétion expérimentale basée sur l’analyse de types.
  • IRB a également fait l’objet d’un vaste refactoring pour faciliter les améliorations futures et a reçu des dizaines de corrections de bugs.

Problèmes de compatibilité

  • L’appel à it sans argument dans un bloc n’est plus utilisé et fera référence au premier paramètre du bloc dans Ruby 3.4.
  • Les variables d’environnement obsolètes sont supprimées.

Mises à jour de la bibliothèque standard

  • RubyGems et Bundler affichent un avertissement si l’utilisateur demande les gems suivantes sans les avoir ajoutées au Gemfile ou au gemspec, car elles deviendront des bundled gems dans une future version de Ruby.
  • Les gems par défaut suivants sont ajoutés ou mis à jour : prism 0.19.0, RubyGems 3.5.3, abbrev 0.1.2, entre autres.
  • Les bundled gems suivants sont promus depuis les default gems ou mis à jour : racc 1.7.3, minitest 5.20.0, entre autres.

Avis de GN⁺

  • Introduction du parseur Prism : l’une des caractéristiques les plus importantes de Ruby 3.3.0 est l’introduction du nouveau parseur Prism. Il aidera grandement les développeurs Ruby en fournissant un parseur plus efficace, robuste face aux erreurs et facile à maintenir.
  • Améliorations de performance de YJIT : les principaux gains de performance de YJIT amélioreront fortement la vitesse d’exécution des applications Ruby, et en particulier la réduction de l’usage mémoire et les optimisations du GC auront un effet positif sur les performances et la stabilité des grandes applications Ruby.
  • Ordonnanceur de threads M:N : l’introduction de l’ordonnanceur de threads M:N a le potentiel d’améliorer les performances des applications Ruby multithreadées. Elle réduira le coût de gestion des threads et permettra un traitement parallèle plus efficace.

1 commentaires

 
GN⁺ 2023-12-26
Avis sur Hacker News
  • Avec l'arrivée de Ruby 3.3, Ruby, un langage qui met l'accent sur le bonheur des développeurs, se débarrasse de son ancienne image de lenteur et affiche désormais de hautes performances.

    • Les performances de Ruby se sont nettement améliorées grâce à des innovations comme YJIT, les object shapes et les optimisations du GC.
    • De grandes entreprises utilisatrices de Ruby, comme Shopify, constatent les gains de performances de Ruby 3.3.
    • Se dit personnellement très enthousiaste pour l'avenir de Ruby et se réjouit d'appliquer Ruby 3.3 aux sites de production de ses clients.
  • Ruby 3.3 est considéré comme la release la plus importante et la plus riche en fonctionnalités des dix dernières années, et l'auteur exprime sa surprise que Ruby ait livré un JIT avant Python.

    • Diverses fonctionnalités comme Prism, Lrama et IRB ont été discutées dans une précédente soumission sur Hacker News.
    • Des fonctionnalités comme Ractor, le planificateur de threads M:N, Fibre et Async n'ont pas été suffisamment évoquées dans le contexte de Rails, et l'auteur aimerait entendre les retours de personnes qui les utilisent en production.
  • Indique que Ruby 3.3 est disponible sur Heroku.

  • Mentionne que le langage Ruby sort une nouvelle release chaque année à Noël.

  • Pose la question de savoir s'il vaut la peine d'apprendre Ruby lorsqu'on connaît déjà Python et NodeJS. Ruby lui paraît séduisant, mais difficile.

  • La résolution de noms, comme avec Socket.getaddrinfo, peut être bloquante. Un worker pthread est créé à chaque fois qu'une résolution de noms est nécessaire afin d'exécuter getaddrinfo(3).

    • Demande si d'autres runtimes de langage font quelque chose de similaire. La création de threads peut sembler coûteuse, mais d'après les benchmarks, l'overhead est minime.
  • Trouve Prism intéressant. Demande s'il existe des exemples d'utilisation de Prism comme outil d'analyse de code Ruby.

  • La variable d'environnement RUBY_MAX_CPU=n définit le nombre maximal de threads natifs. La valeur par défaut est 8.

    • S'interroge sur le fait que la valeur par défaut devrait peut-être être égale au nombre de cœurs logiques, comme dans Tokio de Rust et de nombreux autres runtimes M:N.
  • Cherche de bons exemples utilisant Prism. Se dit déçu de ne pas voir grand-chose sur la page de release en dehors des « API notables ».

  • Mentionne que c'est le cadeau de Noël parfait.