Релиз Ruby 3.3.0: что появилось нового в языке программирования и что изменилось
На днях появился новый релиз динамического объектно ориентированного языка программирования 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-модулей.