Шапка

9.1 Поддержка традиционных языков

Изначально областью использования стековых машин было управление в режиме реального времени. Высокий уровень системной интеграции, достижимый в них, позволяет использовать такие вычислительные устройства в качестве недорогих карт сопроцессоров для персональных компьютеров или рабочих станций начального уровня. Такие сопроцессоры могут специализироваться на решении каких-либо конкретных проблем или даже на поддержке конкретного программного пакета. От обоих вариантов требуется по большей части исполнять код, написанный на традиционных языках программирования.

Поддержка таких языков в стековых машинах реализуется очень легко [* данное заявление слегка контрастирует с доводами главы ... ] . Единственной проблемой будет не слишком оптимальная работа чисто стековых машин при выполнении программ, написанных на традиционных языках в привычном стиле. Это самое заметное различие между возможностями стековых машин и требованиями языка. В обычных программах стремятся уменьшить число вызовов функций и делают упор на локальные переменные. Стековые машины эффективнее выполняют программы с большим числом вызовов процедур и малым числом локальных переменных. Отчасти различия появляются в результате поощрения такого стиля общей практикой программирования и структурой традиционных языков. В известной мере разница состоит в том, что регистровые машины лучше подходят для обработки данных общего вида, а стековые - для управляющего оборудования реального времени. В любом случае, эффективность выполнения программ на обычных языках стековыми машинами можно довести до уровня регистровых и даже превысить его на любых приложениях и при умеренной дополнительной аппаратной поддержке. Общая идея - заимствовать возможности регистров, сохраняя преимущества стека.

9.1.1 Стековые кадры

Таким образом, вопрос состоит в распознании структур языка высокого уровня, которым необходима дополнительная аппаратная поддержка. Удивительно, но конструкция стека времени исполнения в таких языках - единственная важная область, в которой классические стековые машины не могут оказать какую-либо поддержку. Это происходит, потому что языки высокого уровня оперируют понятием «активационная запись» , которая является блоком элементов - «кадром» , сохраняемым в программно-управляемом стеке в программной памяти при каждом вызове подпрограммы. В общепринятой реализации каждый стековый кадр заводится только один раз как большой кусок памяти в прологе вызываемой подпрограммы. Кадр содержит входные параметры ( которые заполняются вызывающим кодом ), объявляемые пользователем локальные переменные и любые промежуточные значения, необходимые компилятору. В ходе выполнения процедура обращается к содержимому кадра, минуя операции сохранения в стеке и выталкивания из него.

Стековые кадры используются для временного хранения данных при вызовах подпрограмм, а не результатов последовательных вычислений. Это означает, что кадры не совместимы с аппаратными стеками уже рассмотренных машин. Очевидным выходом, отвечающим указанным требованиям, будет модификация стековой архитектуры таким образом, чтобы пространство стека можно было распределять достаточно большими блоками, допускающими произвольный доступ к своему содержимому. Выше представлено точное описание решения проблемы в RISC архитектурах с регистровым окном ( классификационная группа SL2 ). Для стековых машин решение не подходит, так как прямой доступ к данным требует указывать в инструкции операнды.

Альтернативой может служить второй аппаратный стек для обращения к локальным переменным с умеренной скоростью, используя первый аппаратный стек для работы с данными. Это позволяет использовать лучшее из обеих архитектур, хотя и не бесплатно. Хорошие характеристики в такой конфигурации можно получить, только если глубина стека для регистровых кадров будет в 5-10 раз больше глубины стека данных.

9.1.2 Отображение регистров на память

Если бы дискуссия на этом заканчивалась, то мы по-прежнему склонялись бы к размещению стековых кадров внутри процессора. Но есть ещё один аргумент, который может сместить равновесие в сторону программной памяти. Таким дополнительным аргументом является семантика традиционных языков, позволяющая обращаться к локальным переменным по адресу. Очень характерен в этом смысле язык Си, создающий проблемы как регистровым, так и стековым машинам.

Отображением регистров на адресное пространство памяти может заниматься хитрая аппаратная схема или умный компилятор, однако такое решение не соответствует философии построения стековых машин: максимум производительности при минимуме сложности. Таким образом, для стековых процессоров наилучшим выбором будет размещение стековых кадров традиционных языков в программной памяти и адресации их через аппаратный указатель кадра . Если места на кристалле достаточно, то для ускорения доступа к кадрам адресное пространство программной памяти можно расширить на внутрипроцессорную оперативную память.

Довольно заманчиво выглядят сложные компиляторы, которые размещают большую часть локальных переменных, создаваемых традиционными языками, в аппаратном стеке. Эксперименты автора на нескольких стековых машинах показали, что разница между теми компиляторами Си, которые хранят локальные переменные в аппаратном стеке, и теми, которые используют для них программную память с адресацией через аппаратный указатель кадра, невелика. Процессоры с аппаратным указателем кадра работают чуть быстрее.

Можно подумать, что сохранять локальные переменные в стеке данных быстрее, чем размещать их в программной памяти. Это не так, потому что стековые машины довольно неэффективны при обращении к данным, скрытым в глубине стека, особенно в том случае, когда содержимое буфера сброшено во внешнюю память. Большую часть времени система, построенная по принципу «всё только в аппаратных стеках», будет тратить на перетасовывание их содержимого. Хотя в общем случае доступ к элементам кадра в памяти медленнее, чем к переменным в аппаратном стеке, суета при работе с данными в стеке пагубно сказывается на быстродействии.

9.1.3 Стратегия обращения со стековыми кадрами

Неплохим вариантом было бы сочетание методов, характерных для кадров в памяти и кадров в стеке данных. Разберём детали. Все вызовы процедур помещают передаваемые параметры в аппаратный стек. Так как эти параметры требуют обсчёта или перемещения из одного кадра в другой, что в любом случае делается с помощью аппаратного стека [* мы же о стековых машинах говорим ] , дополнительного времени на эту операцию не тратится [* т.е. сунули в стек и вызвали подпрограмму, и так всегда - никаких лишних действий, поэтому и «дополнительного не тратится» ] . Затем выполняется вызов. Вызываемая процедура копирует все параметры, за исключением одного или двух, из аппаратного стека в только что выделенный кадр в качестве локальных переменных. В стеке должны оставаться параметры, к которым нет обращений по прямому адресу, о чём надо специально заботиться. Это может делать программист через объявления регистровых переменных, а может и анализ в компиляторе, либо, не мудрствуя лукаво, можно отправить все параметры в память, что проще и безопаснее всего. Один или два элемента, остающихся в стеке, минимизируют сбросы буферов в память и дают неплохое увеличение эффективности. По завершении процедуры возвращаемое значение помещается в аппаратный стек.

Компромиссная стратегия обещает неплохую эффективность, экономя множество обращений к памяти. Кроме того, она обеспечивает отличное взаимодействие между различными языками, например, FORTH и Си. Создание компилятора, реализующего данную идею, относительно несложно. Кроме того, множество уже существующих стековых машин имеет аппаратный указатель кадра и могут воспользоваться описанной техникой.

9.1.4 Эффективность исполнения традиционных языков

Из обсуждения видно, что стековые машины могут иметь неплохую эффективность при поддержке традиционных языков, использующих стековые кадры. Конечно, здесь стековые процессоры с аппаратным указателем кадра не могут быть столь же эффективны, как RISC машины с большим внутрипроцессорным регистровым файлом, прямой поддержкой кадров и очень умными компиляторами, выполняющими сложную работу по перераспределению регистров.

Программы, написанные на традиционных языках, соответствуют вычислительной модели регистровых машин, в рамках которой стековые машины будут недостаточно эффективны. К программным причинам снижения производительности относятся: большие области последовательных команд, близкая к 100% доля кэш-попаданий [* увеличивает скорострельность регистровых ] , большое число локальных переменных, используемых в больших процедурах, и подпрограммы с малым уровнем вложенности, допускающих компиляцию во «встроенный» код.

Для того чтобы в программах использовались структуры, которые эффективно обрабатываются стековыми машинами, последние должны быть столь же или даже более производительны, чем регистровые машины. Код хорошо подходящий стековым машинам включает: процедуры с высокой степенью фрагментации и множеством уровней вложенности, возможно, рекурсивные; относительно небольшое число часто используемых подпрограмм и внутренних циклов, которые можно разместить в быстрой памяти или обеспечить поддержку микрокодом; небольшое число локальных переменных, передаваемых через множество уровней вызовов подпрограмм; вызовы процедур с глубокой вложенностью. И, кроме того, от использования стековых машин могут выиграть программы, предназначенные для работы в условиях частых прерываний и переключения контекста.

Практический способ использования обычных языков в стековых компьютерах состоит в заимствовании привычного подхода при написании основного объёма кода программы на языке высокого уровня. Затем внутренние циклы переписываются на ассемблере целевой стековой машины. Такой путь обеспечивает очень высокую производительность при умеренном объёме работы. В том случае, когда востребованы отличные параметры стековых машин при работе в режиме реального времени, описанный путь обеспечивает наилучшее соотношение скорости работы к вложенным программистом усилиям.

Следует также учитывать, что существуют причины для выбора процессора, отличающиеся от скорости выполнения конкретной программы. Соображения, могущие сместить интерес в сторону стековых машин, включают: скорость обработки прерываний, скорость переключения задач, низкая сложность вычислительной системы, потребность в специфическом для конкретной задачи микрокоде или аппаратной поддержке. Подводя итог, можно отметить, что стековые машины не смогут, возможно, исполнять большую часть программ на традиционных языках столь же быстро, как регистровые машины. Но их слабости [* ну вот, приехали: «слабости» стековых машин ]   могут в известной степени компенсироваться дополнительными соображениями, делая стековые процессоры отличной альтернативой, особенно в приложениях реального времени.

Previous part:

Next part: