Support de WebAssembly basé sur WASI
Il s'agit d'un portage initial du support WebAssembly basé sur WASI. Cela permet à un binaire CRuby d'être disponible sur un navigateur Web, un environnement Serverless Edge, ou d'autres types d'embedders WebAssembly/WASI. Actuellement, ce portage passe les suites de tests de base et d'amorçage n'utilisant pas l'API Thread.
Intégration
WebAssembly (Wasm) a été introduit à l'origine pour exécuter des programmes rapidement et en toute sécurité dans les navigateurs web. Mais son objectif - exécuter des programmes efficacement et en toute sécurité dans divers environnements - est recherché depuis longtemps non seulement pour le web mais aussi par des applications générales. WASI (The WebAssembly System Interface) est conçu pour de tels cas d'utilisation. Bien que ces applications aient besoin de communiquer avec les systèmes d'exploitation, WebAssembly fonctionne sur une machine virtuelle qui n'avait pas d'interface système. La WASI la normalise.
La prise en charge de WebAssembly/WASI dans Ruby vise à tirer parti de ces projets. Il permet aux développeurs Ruby d'écrire des applications qui fonctionnent sur ces plateformes.
Cas d'utilisation
Ce support encourage les développeurs à utiliser CRuby dans un environnement WebAssembly. Un exemple d'utilisation est le support CRuby du playground TryRuby. Vous pouvez maintenant essayer CRuby original dans votre navigateur Web.
Points techniques
Le WASI d'aujourd'hui et WebAssembly lui-même manquent de certaines fonctionnalités pour mettre en œuvre Fiber, exception, et GC parce qu'il est encore en cours de développement, et aussi pour des raisons de sécurité. CRuby comble donc ce manque en utilisant Asyncify, qui est une technique de transformation binaire pour contrôler l'exécution en userland.
En outre, l’équipe Ruby a construit un VFS au-dessus de WASI afin de pouvoir facilement regrouper les applications Ruby dans un seul fichier .wasm. Cela rend la distribution des applications Ruby un peu plus facile.
Le YJIT prêt pour la production
YJIT est un JIT Ruby léger et minimaliste construit dans CRuby. Il compile le code en utilisant une architecture BBV (Basic Block Versioning). Le cas d'utilisation visé est celui des serveurs exécutant Ruby on Rails, un domaine où MJIT n'a pas encore réussi à fournir des accélérations. YJIT est actuellement pris en charge pour macOS et Linux sur les processeurs x86-64 et arm64/aarch64. Ce projet est open source et tombe sous la même licence que CRuby.
- YJIT n'est plus expérimental ;
- Il a été testé sur des charges de travail de production pendant plus d'un an et s'est avéré très stable ;
- Il a été testé sur des charges de travail de production pendant plus d'un an et s'est avéré très stable ;
- Il prend désormais en charge les processeurs x86-64 et arm64/aarch64 sur Linux, MacOS, BSD et d'autres plateformes UNIX ;
- Cette version apporte le support pour Apple M1/M2, AWS Graviton, Raspberry Pi 4 et plus ;
- Cette version apporte le support pour Apple M1/M2, AWS Graviton, Raspberry Pi 4 et plus ;
- La construction de YJIT nécessite maintenant Rust 1.58.0+ ;
- Afin de s'assurer que CRuby est construit avec YJIT, veuillez installer rustc >= 1.58.0 avant d'exécuter le script ./configure ;
- Afin de s'assurer que CRuby est construit avec YJIT, veuillez installer rustc >= 1.58.0 avant d'exécuter le script ./configure ;
- La version 3.2 de YJIT est plus rapide que la version 3.1, et a environ un tiers de moins de surcharge mémoire.
- Globalement, YJIT est 41% plus rapide (moyenne géométrique) que l'interpréteur Ruby sur yjit-bench ;
- La mémoire physique pour le code JIT est allouée paresseusement. Contrairement à Ruby 3.1, le RSS d'un processus Ruby est minimisé car les pages de mémoire virtuelle allouées par --yjit-exec-mem-size ne seront pas mappées en pages de mémoire physique avant d'être effectivement utilisées par le code JIT ;
- Introduire la GC du code qui libère toutes les pages de code lorsque la consommation de mémoire par le code JIT atteint --yjit-exec-mem-size ;
- RubyVM::YJIT.runtime_stats renvoie des mesures de Code GC en plus des clés inline_code_size et outlined_code_size existantes : code_gc_count, live_page_count, freed_page_count, et freed_code_size.
- La plupart des statistiques produites par RubyVM::YJIT.runtime_stats sont maintenant disponibles dans les builds de version ;.
- Il suffit d'exécuter ruby avec --yjit-stats pour calculer et afficher les statistiques (ce qui entraîne une surcharge de temps d'exécution) ;
- YJIT est désormais optimisé pour tirer parti des formes des objets.
- Profiter de l'invalidation des constantes à grain fin pour invalider moins de code lors de la définition de nouvelles constantes. [Fonctionnalité n°18589]
- La valeur par défaut de --yjit-exec-mem-size est passée à 64 (MiB).
- Le seuil d'appel par défaut --yjit-call-threshold est modifié à 30.
Améliorations de Regexp contre ReDoS
Il est connu que la correspondance des expressions régulières peut prendre un temps considérable. Si le code tente de faire correspondre une Regexp potentiellement inefficace avec une entrée non fiable, un attaquant peut l'exploiter pour un déni de service efficace (appelé expression régulière DoS, ou ReDoS). L’équipe Ruby a introduit deux améliorations qui atténuent considérablement les ReDoS.
Algorithme de correspondance Regexp amélioré
Depuis Ruby 3.2, l'algorithme de correspondance de Regexp a été grandement amélioré en utilisant une technique de mémorisation.
# This match takes 10 sec. in Ruby 3.1, and 0.003 sec. in Ruby 3.2
/^a*b?a*$/ =~ "a" * 50000 + "x"
L'algorithme de correspondance amélioré permet de réaliser la plupart des correspondances Regexp (environ 90 %) en temps linéaire.
Cette optimisation peut consommer de la mémoire proportionnellement à la longueur de l'entrée pour chaque correspondance. Une correspondance Regexp normale devrait consommer au maximum 10 fois plus de mémoire que la longueur de l'entrée.
Délai d'attente pour les Regexp
L'optimisation ci-dessus ne peut pas être appliquée à certains types d'expressions régulières, comme celles qui incluent des fonctionnalités avancées (par exemple, des références arrière ou des recherches), ou qui comportent un nombre fixe important de répétitions. En guise de mesure de repli, une fonction de délai d'attente pour les correspondances Regexp est également introduite.
Regexp.timeout = 1.0
/^a*b?a*()\1$/ =~ "a" * 50000 + "x"
#=> Regexp::TimeoutError is raised in one second
Notons que Regexp.timeout est une configuration globale. Pour utiliser des paramètres de délai différents pour certaines Regexp spéciales, il est possible d’utiliser le mot clé timeout pour Regexp.new.
Regexp.timeout = 1.0
# This regexp has no timeout
long_time_re = Regexp.new('^a*b?a*()\1$', timeout: Float::INFINITY)
long_time_re =~ "a" * 50000 + "x" # never interrupted
Autres nouveautés notables
SyntaxSuggest
La fonctionnalité de syntax_suggest (anciennement dead_end) est intégrée à Ruby. Elle aide à trouver la position d'erreurs telles que des fins manquantes ou superflues, afin de remettre en route plus rapidement, comme dans l'exemple suivant :
Unmatched `end', missing keyword (`do', `def`, `if`, etc.) ?
1 class Dog
> 2 defbark
> 3 end
4 end
ErrorHighlight
Il indique maintenant le ou les arguments pertinents pour TypeError et ArgumentError.
test.rb:2:in `+': nil can't be coerced into Integer (TypeError)
sum = ary[0] + ary[1]
^^^^^^
Source : Ruby
Et vous ?
Avez-vous testé la nouvelle version de Ruby ?
Quel est votre avis sur les nouvelles fonctionnalités et améliorations apportées ?
Voir aussi :
Ruby 2.6 est disponible en version stable, avec un nouveau compilateur JIT en mode expérimental et un nouveau module pour analyser du code Ruby
Ruby 2.7 est disponible en version stable, avec l'ajout du filtrage par motif, d'une nouvelle méthode pour compacter le tas et plusieurs améliorations de performance non négligeables