Почему невыполненный код времени компиляции увеличивает размер байт-кода Raku? Это снижает производительность во время выполнения?

Рассмотрим следующие две программы:

unit module Comp;
say 'Hello, world!'

а также

unit module Comp;
CHECK { if $*DISTRO.is-win { say 'compiling on Windows' }}
say 'Hello, world!'

Наивно, я ожидал, что обе программы будут компилироваться в один и тот же байт-код: блок CHECK указывает код для запуска в конце компиляции; проверка переменной и последующее бездействие не влияет на поведение программы во время выполнения, и поэтому (я бы подумал) не нужно включать в скомпилированный байт-код.

Однако компиляция этих двух программ не приводит к одному и тому же байт-коду. В частности, компиляция версии без блока CHECK создает 24 КБ байт-кода по сравнению с 60 КБ для версии с ним. Почему байт-код этих двух версий отличается? Имеет ли эта разница в байт-коде (или потенциально имеет) затраты на выполнение? (Вроде должно, но я хочу быть уверенным).

И еще один связанный с этим вопрос: как блоки DOC CHECK вписываются в приведенное выше? Насколько я понимаю, даже компилятор пропускает DOC CHECK блоки, если он не запущен с флагом --doc. В соответствии с этим, байт-код для программы hello-world не увеличивается в размере, когда ему задан блок DOC CHECK, подобный приведенному выше. Однако он действительно увеличивается в размере, если блок включает инструкцию use. Из этого я делаю вывод, что use имеет какой-то особый характер и выполняется даже в DOC CHECK блоках. Это верно? Если да, то есть ли другие формы с таким же специальным регистром, о которых мне следует знать?


person codesections    schedule 26.08.2020    source источник


Ответы (1)


Блок CHECK или BEGIN (или другие BEGIN-временные конструкции) могут содержать код, который экранируется. Например:

BEGIN SomeClass.^add_method('foo', anon method foo() { 42 })

Добавляет метод в класс, который существует за пределами блока BEGIN. Следовательно, байт-код этого метода требуется в скомпилированном выводе. В настоящее время Rakudo консервативно включает байт-код всего в блоке BEGIN или CHECK. Возможно, в будущем удастся избежать этого для некоторых простых случаев.

Что касается затрат времени выполнения, реализация идет на некоторые длины, чтобы минимизировать стоимость байт-кода, который никогда не запускается (не столько в этом случае, сколько потому, что стандартная библиотека огромна, но многие программы используют только ее часть). Например:

  • Байт-код mmap'd, поэтому некоторые неиспользуемые его части могут не быть выгружены в память.
  • Байт-код проверяется только при первом вызове этого кадра
  • Метаданные кадра (какие лексические слова у него есть) десериализуются только при первом обращении к кадру.
  • Если что-то не ссылается на него, объект кода не будет десериализован.

Что касается use, его действие выполняется сразу после его анализа. Пребывание внутри блока DOC CHECK не подавляет это - и, как правило, не может, потому что use может вносить вещи, которые необходимо знать, чтобы завершить синтаксический анализ содержимого этого блока.

person Jonathan Worthington    schedule 26.08.2020
comment
Я не совсем уверен, что понимаю первую часть вашего ответа. Учитывая файл с class SomeClass {}, я понимаю, что добавление BEGIN SomeClass.^add_method('foo', anon method foo() { 42 }) имеет эффект времени выполнения - SomeClass теперь имеет метод foo, тогда как раньше этого не было. Но это тот же эффект времени выполнения, что и объявление класса с class SomeClass { method foo { 42 }, и я (опять же, наивно) ожидал, что они будут компилироваться в один и тот же байт-код, точно так же, как макрос компилируется в тот же код, что и расширенная версия макроса. Но две формы генерируют разный байт-код. Что мне не хватает? - person codesections; 26.08.2020
comment
Кроме того, большое спасибо за время, которое вы потратили на ответы на вопросы Раку здесь. Из последних трех вопросов, которые я задал, вы ответили на все три - и в каждом случае дали очень полезный ответ, который я не мог бы получить ни от кого, если бы не был хорошо знаком с Раку / Ракудо. Я открыл вопрос, чтобы добавить информацию из этого вопроса в документы Raku и, как только я немного разберусь в этой области, я позабочусь о том, чтобы информация попала в документацию. - person codesections; 26.08.2020
comment
@codesections Когда есть конструкция BEGIN-time, она дает вложенный бит времени компиляции, за которым следует время выполнения блока BEGIN, и только после этого возобновляется синтаксический анализ окружающего времени компиляции. Окончательный результат компиляции каким-то образом должен объединить их; Я подозреваю, что вы в основном видите побочные эффекты от этого. - person Jonathan Worthington; 26.08.2020