На днях появился новый релиз динамического объектно ориентированного языка программирования Ruby 3.3.0. Он вобрал в себя лучшие возможности многих других ЯП, включая Perl, Java, Python, Smalltalk, Eiffel, Ada и Lisp. Что касается кода проекта, то он распространяется под лицензиями BSD (2-clause BSDL) и Ruby, которая ссылается на последний вариант лицензии GPL и полностью совместима с GPLv3. Подробности — под катом.

Что изменилось и что добавили?

  • Одно из важных изменений — оптимизация производительности JIT-компилятора YJIT. Он развивается разработчиками платформы электронной коммерции Shopify. Делается это в рамках проекта по улучшению производительности Ruby-программ, которые используют фреймворк Rails и вызывают очень много методов. YJIT использует версионирование базовых блоков (LBBV — Lazy Basic Block Versioning) вместо обработки методов целиком и реализован в форме интегрированного JIT-компилятора, написанного на языке Rust. Благодаря LBBV JIT вначале компилирует только начало метода, а оставшуюся часть компилирует через некоторое время, после того как в процессе выполнения будут определены типы используемых переменных и аргументов.

  • При проведении тестов, включая выполнение эмулятора Optcarrot, производительность при использовании компилятора примерно в три раза выше выполнения посредством интерпретатора. Также стоит отметить, что в новой версии обеспечено выделение регистров для стековых операций виртуальной машины. Кроме того, расширен спектр компилируемых вызовов с необязательными аргументами, реализовано inline-развёртывание базовых и специализированных методов, добавлены отдельные оптимизации для операций "Integer#*", "Integer#!=", "String#!=", "String#getbyte", "Kernel#block_given?", "Kernel#is_a?", "Kernel#instance_of?" и "Module#===". Значительно увеличена скорость компиляции.

  • А ещё снижено потребление памяти на хранение метаданных плюс обеспечена генерация более компактного кода для архитектуры ARM64. Также отключён сборщик мусора Code ("--yjit-code-gc"), динамически высвобождающий неиспользуемый сгенерированный машинный код, но приводящий к проседанию производительности во время сборки мусора. Появился новый метод RubyVM::YJIT.enable для управления включением YJIT во время работы, без необходимости запуска с определённым параметром командной строки или переменной окружения. Ну и ко всему реализовано расширение статистики, которая выдаётся при указании опции "--yjit-stats". Добавлены режимы профилирования производительности (--yjit-perf) и трассировки (--yjit-trace-exits).

  • Также в основном составе появился парсер Prism. Он добавлен в виде Си-библиотеки libprism, задействованной в интерпретаторе CRuby, и gem-пакета на языке Ruby, предоставляющего общедоступный API для нисходящего рекурсивного разбора кода на языке Ruby, пригодный для использования в рабочих проектах вместо парсера Ripper. Плюс парсера в гибкой обработке ошибок в коде. Для того чтобы включить парсер, нужно воспользоваться опцией "ruby --parser=prism" или переменной окружения RUBYOPT="--parser=prism". Для разбора кода в программах есть методы Prism.parse(source) для получения AST-представления кода, Prism.parse_comments(source) для выделения комментариев из кода и Prism.parse_success?(source) для проверки наличия ошибок в коде.

  • Ранее для генерации парсеров использовался внешний пакет Bison. Теперь вместо него используется Lrama, который предоставляет реализацию алгоритма LALR на Ruby. Он поддерживает Bison-совместимые определения грамматик (parse.y), используемые в CRuby, реализует расширенные возможности, такие как обработка ошибок и параметризованные правила (?, *, +).

  • Добавлен новый JIT-компилятор RJIT. Он написан целиком и полностью на Ruby. Его достоинство — отсутствие необходимости работы с Си-компилятором. RJIT поддерживает только архитектуру x86-64 и Unix-подобные платформы.

  • Появился планировщик потоков "M:N". Он позволяет для сокращения накладных расходов на создание и управление потоками использовать ограниченное число потоков операционной системы для обработки потоков в коде на языке Ruby. По дефолту применяется 8 потоков ОС (можно изменить через переменную окружения RUBY_MAX_CPU). Использование планировщика "M:N" может привести к проблемам с совместимостью с расширениями на языке Си, поэтому он отключён по умолчанию для основного (main) класса Ractor, но включён для неосновных (non-main). Для того чтобы включить планировщик принудительно, нужно выставить переменную окружения RUBY_MN_THREADS=1.

  • Также расширены возможности оболочки интерактивных вычислений IRB(REPL, Read-Eval-Print-Loop). Например, добавлено вычисление IRB(REPL, Read-Eval-Print-Loop). А ещё появилась поддержка многостраничного просмотра вывода команд ls, show_source и show_cmds. Реализована экспериментальная поддержка автоматического дополнения ввода, учитывающая типы данных. Представлены команды для изменения цвета и стиля шрифта.

  • Разработчики решили объявить устаревшим вызов метода "it" без аргументов в блоке без параметров (например, "[1, 2, 3].each { puts it }").

  • Также в RubyGems и Bundler активировано появление предупреждения в случае указания в "require" gem-пакетов abbrev, base64, bigdecimal, csv, drb, getoptlong, mutex_m, nkf, observer, racc, resolv-replace, rinda и syslog, если они не добавлены в Gemfile или gemspec. В будущих версиях Ruby данные gem-пакеты будут встроены в основной состав.

  • Наконец, обновлены все версии встроенных и входящих в стандартную библиотеку gem-модулей.

Комментарии (2)


  1. AndreyMorozov
    11.01.2024 02:30
    +3

    Спасибо. Очень недооценённый ЯП.


  1. reiser
    11.01.2024 02:30

    Разработчики решили объявить устаревшим вызов метода "it" без аргументов в блоке без параметров (например, "[1, 2, 3].each { puts it }").

    Какой-то невнятный перевод.

    Да, вызов it в блоке без параметров действительно объявлен устаревшим, но как раз для того, чтобы сделать его ссылкой на первый параметр и можно было бы писать

    ```

    [1, 2, 3].each { puts it }"

    ```

    вместо

    ```

    [1, 2, 3].each { |x| puts x }"

    ```

    https://bugs.ruby-lang.org/issues/18980