554
Глава 5. Вычисления на регистровых машинах
Сравните этот пример с вычислением (factorial 5) с помощью интерпретируемой
вер сии той же самой процедуры, приведенным в конце раздела 5.4.4. В интерпретируемой
вер сии потребовалось 144 сохранения и максимальная глубина стека 28. Это показывает,
какую оптимизацию удалось получить с помощью нашей стратегии компиляции.
Интерпретация и компиляция
При помощи программ из этого разде ла мы можем проводить эксперименты с различ-
ными стратегиями выполнения: интерпретацией и компиляцией
51
. Интерпре татор подн и-
мает машину до уровня пользовательской программы; компил ятор опускает пользова-
тельскую программу до уровня машинного языка. Можно рассматривать язык Scheme
(или любой язык программирования) как согласованную систему абстракций, построен-
ных поверх машинного языка. Интерпретаторы хороши для интерактивной разработки
программ и их отладки, поскольку шаги выполнени я программы организованы в терми-
нах этих абстракций, и, следовательно, лучше понятны программисту. Скомпилирован-
ный код может работать быстрее, поскольку шаги выполнения программы организованы
в терминах машинного языка, и компил ятор может проводить оптимизации, которые
нарушают абстракции верхнего уровня
52
.
Компиляция и интерпретация также ведут к различным стратегиям при переносе
языков на новые компьютеры. Предположим, что нам надо реализовать Лисп для новой
машины. Одна страте ги я будет состоять в том, чтобы взять вычислитель с явным управ-
лением из раздела 5.4 и перевести е го команды в команды новой машины. Вторая — в
том, чтобы нач ать с компилятора и изменить генераторы кода так, чтобы они порожда-
ли код новой машины. Вторая стратегия позволяет запускать на новой машине любую
программу на Лиспе, если сначала скомпи лировать ее компилятором, который работает
на исходной Лисп-системе, а затем связать со скомпилированной версией рабочей биб-
лиотеки
53
. Более того, мы можем скомпилировать сам компилятор, и, запустив его на
51
Можно добиться даже большего, если расширить компилятор так, чтобы скомпилированный код мог вы-
зывать интерпретируемые процедуры. См. упражнение 5.47.
52
Независимо от стратегии выполнения, мы сталкиваемся с существенным замедлением, если требуем, что-
бы ошибки, возникающие при выполнении пол ьзовательской программы, были обнаружены и отмечены, а не
приводили к смерти системы или к неверным результатам работы. Например, индексирование за границы мас-
сива можно обнаружить, если перед обращением к массиву проверить правильность индекса. Однако затраты
на проверку могут быть во много раз больше, чем стоимость самого обращения, и программисту приходится
взвешивать преимущества скорости и безопасности, когда он решает, нужна ли такая проверка. Хороший ком-
пилятор должен уметь порождать код с проверками, избегать лишних проверок и позволять программистам
управлять количеством и видами проверок на ошибки в скомпил ированном коде.
Компиляторы для популярных языков программирования, вроде C или C++, почти никаких проверок в ра-
ботающий код не помещают, чтобы программы выполнялись как можно быстрее. В результате программистам
приходится самим проводить проверку на ошибки. К сожалению, многие этим пренебрегают, даже в крити-
ческих приложениях, где скорость не является существенным ограничением. Их программы ведут бурную и
опасную жизнь. Например, знаменитый «Червь», который парализовал Интернет в 1988 году, использовал то,
что операционная система UNIX
TM
не проверяла переполнение буфера в демоне finger. (См. Spafford 1 989.)
53
Разумеется, как при интерпретаци и, так и при компиляции придется еще реализовать для новой машины
управление памятью, ввод и вывод, а также все операции, которые мы считали «элементарными» при обсуж-
дении интерпретатора и компилятора. Один из способов минимизировать эту работу заключается в том, чтобы
как можно большее число этих операций написать на Лиспе, а затем скомпилировать для новой машины. В
конце концов все сводится к простому ядру (например, сборка мусора и механизм для применения настоящих
машинных примитивов), которое кодируется для новой машины вручную.