- Автор Phil Winterbottom, Rob Pike
- Оригинал: http://www.vitanuova.com/inferno/papers/hotchips.html
Введение[]
- Виртуальные машины - важный компонент современных переносимых окружений, таких как Inferno и Java, они обеспечивают независимое от архитектуры представление исполняемого кода. Их производительность критична для успеха этих окружений, но их трудно правильно спроектировать, потому что они являются способом достижения противоречивых целей. С одной строны, они предлагают путь сокрытия различий между процессорными архитектурами; с другой - они должны быть реализованы эффективно на множестве машин. Сравнение разработки и эволюции виртуальных машин Inferno и Java позволяет понять компромиссы в их дизайне и реализации. Мы выступаем в пользу того, что дизайн виртуальных машин должен произрастать из сущности современных процессоров, а не интерпретаторов языков, с уклоном в сторону компиляции-на-лету вместо интерпретации или использования специальных чипов.
Dis, Виртуальная Машина Inferno[]
В начале 1995-го мы задумались о применении идей операционной системы Plan9 [1] на более широком диапазоне устройств и сетей. В результате родилась Inferno [2], небольшая операционная система и среда исполнения, которая поддерживает переносимость приложений между широким диапазоном процессоров и операционных систем. Не подозревая об одновременной работе над созданием Java [3], выросшей из технологии проекта Oak, мы независимо пришли к заключению, что виртуальная машина (ВМ) была необходимым компонентом такой системы [4]. Благодаря увеличению скорости работы процессоров и появлению компиляторов-на-лету, ВМ может исполняться достаточно быстро для того, чтобы быть жизнеспособной с экономической точки зрения.
Виртуальная машина Inferno, названная Dis, имеет несколько примечательных аспектов дизайна: набор инструкций, система модулей и сборщик мусора.
Набор инструкций Dis максимально приближен к набору инструкций существующих процессоров. Инструкции имеют следующую форму:
OP src1, src2, dst
Операнды src1 и dst указывают на общие адреса или константы произвольного размера, тогда как операнд src2 ограничен меньшими константами и смещениями в стеке для уменьшения пространства кода. Каждый операнд указывает на адрес, либо на фрейм стека исполняемой процедуры или на глобальные данные своего модуля.
Типы операндов определяются инструкциями. Базовые типы это: слово (word, 32-битное знаковое), большое целое (big, 64-битное знаковое), байт (byte, 8-битное беззнаковое) и указатель (зависит от реализации). Набор инструкций следует примеру CISC-процессоров, предоставляя трех-операндные память-в-память инструкции для арифметических действий, пермещения данных и т.д. Также ВМ имеет инструкции для выделения памяти, загрузки модулей, средства создания, синхронизации и коммуникации между процессами.
Модуль - это единица динамически загруженного кода и данных. Модули загржаются с помощью инструкций ВМ, которые возвращают указатель на таблицу методов (method table) модуля. Этот указатель управляется сборщиком мусора, так что код и данные модуля обрабатываются сборщиком мусора как и любая другая память. Безопасность типов гарантируется с помощью проверки типов методов во время загрузки модуля используя MD5-сигнатуру типа.
Управление памятью тесно связано с набором инструкций ВМ. Dis использует гибридную схему сборки мусора: большая часть мусора собирается с помощью простого подсчета ссылок, в то время как помечающий сборщик собирает данные в режиме реального времени. Поскольку подсчет ссылок точная, а не консервативныя форма сборки мусора, типы всех элементов данных должны быть известны среде исполнения ВМ. Поэтому компилятор с языка в иснтрукции ВМ генерирует дескриптор типа для всех смешанных типов. Этот дескриптор сообщает о местоположении всех указателей внутри типа, позволяя ВМ отслеживать ссылки во время копирования элементов данных.
Сборка мусора[]
Память - один из самых дорогих компонентов небольших систем, поэтому ВМ должна быть спроектирована так, чтобы потреблять настолько мало памяти, насколько возможно. Через сборку мусора, основанную на подсчете ссылок, Dis освобождает память когда она уже не нужна. Подсчет ссылок также устраняет необходимость в больших участках памяти для каждого процесса, которые необходимы помечающему (mark-and-sweep) сборщику для эффективной работы. Оба этих следствия снижают требования ВМ и ее приложений к памяти.
Сравните это с Java ВМ, инструкции которой создают сложножности при отслеживании ссылок на копируемые объекты, поэтому реализации JVM используют более "ленивые" техники вроде пометки, которые используют большие участки памяти и отложенную сборку, оба из которых повышают расход памяти, а следовательно и цену всей системы.
Проблемы компиляции[]
Это просто - исполнять индивидуальные инструкции стековых виртуальных машин (SM), таких как Java (JVM), потому что операнды неявные. Однако, реализация интерпретатора высокоуровневого языка генерирует большие потоки памяти в SM, чем эквивалентный набор инструкций машины память-в-память (memory transfer machine, MM), такой как Dis. Рассмотрим код, который необходимо исполнить:
c = a + b;
SM будет исполнять его с помощью следующего кода (генерируемый поток памяти мы указали с помощью символов L - чтение, и S - запись):
push a # LS push b # LS add # LLS store c # LS
Соответствующий код MM будет простой трех-операндной инструкцией:
add a,b,c # LLS
Во время интерпретации дополнительный поток памяти в SM будет не так важен благодаря выигранному времени, которое не пришлось тратить на декодирование полей операнда. Поля операнда неявные в инструкциях SM, тогда как в MM они явные: три поля операнда должны быть декодированы в каждой инструкции, даже в той, которая не имеет операндов.
Во время компиляции компромиссы другие. Очевидно, что каждый тип ВМ может производить одинаковый набор инструкций с помощью компилятора на-лету (JIT), но в случае SM большая часть работы должна быть проделана самим JIT, тогда как в MM большую часть работы делает фронтенд (компилятор, прим. пер.) и JIT может быть значительно проще и быстрее.
В SM JIT-компилятор вынужден делать большую часть работы по распределению регистров. Поскольку типы ячеек стека изменются по мере исполнения программы, JIT должен отслеживать их типы во время компиляции. Архитектура MM, с другой стороны, хорошо соответствует инструкциям низлежащего процессора. Она использует стратегии последовательного выделения регистров от никакой до простого маппинга известных ячеек на регистры и потокового выделения регистров. Большая часть работы любой из этих стратегий может быть выполнена компилятором с языка программирования в инструкции ВМ. Он может генерировать код для машины с бесконечным числом регистров, а JIT со своей стороны сможет выделять столько регистров, сколько возможно. Все это позволяет сделать JIT простым.
Процессоры[]
Те проблемы, с которыми сталкиваются создатели JIT, так же актуальны и для проетировщиков специальных процессоров, поддерживающих инструкции ВМ. Выделение регистров JIT-компилятором аналогично смене меток регистров в кремнии, и виртуальные машины SM добавляют излишнюю сложность к и без того сложной проблеме. Кто-то может поспорить, что стековый процессор смягчит трудности, но наш опыт реализации стековых машин в AT&T Crisp microprocessor склонил нас к убеждению, что стековые архитектуры медленнее чем регистровые по своей природе. Их дизайн усложняет процесс исполнения кода путем замены простых регистров на комплесный механизм кэширования стека (stack cache mechanism).
Говоря другими словами, подогнать ВМ под процессор - лучшая идея, чем подогнать процессор под ВМ.
Dis соответствует этому критерию лучше, но мы не планируем создавать Dis в кремнии. Идея ВМ в том, чтобы быть независимой от архитектуры процессоров; предложение специального процессора для исполнения ее инструкций сведет на нет изначальную цель по предложению единого набора инструкций. Но если забыть об этом на момент, можно придумать две причины, по которым стоит задуматься о создании Dis в кремнии: производительность и цена.
Если говорить о производительности, то история показывает, что процессоры, привязанные к языку, неконкурентноспособны. Вложение средств в разработку специальных процессоров приводит к трате сил, которые можно было бы использовать для решения системных вопросов, которые в конце-концов доминируют над производительностью. Выгода от производительности, достигнутая через поддержку языка возмещается через параллельные улучшения в процессорах общего назначения на протяжении всего времени жизни процессора.
Инструкции Dis компилируются в машинный код, который всего на 30-50% медленнее кода, сгенерированного с языка Си. Учитывая текущую скорость улучшения процессоров, это всего несколько месяцев разработки процессора. Это показывает, что лучше сфокусироваться на улучшении исполнения на существующих процессорах, чем изобретать новую архитектуру.
Вопрос цены более тонкий. Dis так же близок к привычным архитектурам, построенным на специальном чипе с высокой степенью интеграции систем связи, которые могут быть экономически более выгодными на небольших платформах. Однако, реальная причина этого в том, что дизайн подсистемы управления памятью виртуальной машины делает простым реализацию Dis в небольшом объеме памяти. В противположность этому скажем, что какой бы ни была экономическая выгода от интегированного Java-процессора, она будет потеряна из-за необходимости наращивания памяти, которую требует консервативная схема сборки мусора ВМ Java.
Литература[]
- R. Pike, D. Presotto, S. Dorward, B. Flandrena, K. Thompson, H. Trickey, and P. Winterbottom. "Plan 9 from Bell Labs", J. Computing Systems 8:3, Summer 1995, pp. 221-254.
- Dorward, S., et al., "Inferno", IEEE Compcon 97 Proceedings, 1997.
- Arnold, K. and Gosling, J., The Java Programming Language, Addison-Wesley, 1996.
- Nori, K. V., Ammann, U., Nabeli, H. H., and Jacobi, Ch., "Pascal P Implementation notes", in Barron, D. W. (ed.), PascalߝThe Language and its Implementation, Wiley, 1981, pp. 125-170.
- Ditzel, D. R. and McLellan, R., "Register Allocation for Free: The C Machine Stack Cache", Proc. of Symp. on Arch. Supp. for Prog. Lang. and Op. Sys., March, 1982, pp. 48-56.
- Case, B., "Implementing the Java Virtual Machine", Microprocessor Report, March 25, 1996, pp. 12-17.