Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Свитки
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  17:12[Войти] | [Зарегистрироваться]

Несколько слов о теории и практике компиляции

Михаил Бобров
дата публикации 19-09-2001 16:35

Несколько слов о теории и практике компиляции

Статья из серии "С точки зрения глупого ламера".
Предлагалась в "КГ", но не была напечатана.

Не так давно на форуме linux.org.ru среди прочих тем появилось вот какое заявление:

"Некоторые пользователи Linux могут вернуться к Windows".
Статья, обосновывающая данное утверждение, принадлежала МелкоМягкой Компании. Видимо, поэтому народ на форуме даже не утруждал себя размышлениями и единогласно заклеймил данное утверждение за происки и отрыв от.

Однако W-ME линия за последнее время стала несколько надежней, с чем большинство народа соглашается. А Linux за то же самое время НЕ СТАЛ УДОБНЕЕ, как не стал и дружественнее. Все чаще я ловлю себя на том, что не помню, где именно мне искать ту или иную shared library; да и по-прежнему не слишком просто его настраивать - несмотря на все заявленные графические рюшечки и ленточки. Поэтому, дамы и господа пингвины, исход данного спора отнюдь не кажется мне таким уж ясным. Билли сдаваться не собирается, чему подтверждением и служат недавно вываленные на open source обвинения в якобы вирусной опасности последних. Если Сообщество не отреагирует на развитие W- ME каким-либо образом, то некоторые пользователи Linux действительно могут перейти на Windows.

Это была присказка. Сказка впереди, и заключается она в том, что все 233КВ обсуждения вышеупомянутого вопроса на форуме linux.org.ru посвящены совсем другому! То есть, один большой сплошной оффтопик с редкими вкраплениями вопиющих об нарушении. Чему же тогда эти килобайты посвящены? Вечному вопросу о том, какой язык программирования лучше, С++ или другой.

Так я наглядно убедился в следующих вещах:

  1. Отношение "сигнал-шум" в FIDO на порядок выше инетовского.
  2. Модератор - совершенно необходимая роль.
  3. Анонимность поощряет хамство.
  4. Спор о языках вечен.
И, раз уж зашла речь о языках, я подумал, что надо же решить хотя бы для себя: а, действительно, который же из них лучше? А то попадешь в компанию, не дай Бог, настоящих программистов, да и лопухнешься. Вот, собственно, мы и подошли к заглавию статьи и скажем пару слов о компиляции.

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

Теперь посмотрим на языки, которые джентльмены с linux.org.ru пытались сравнивать между собой.

Помянутые языки четко разделяются на три группы:

  1. Языки с ярко выраженным функциональным подходом. Haskel, LISP, CLOS.
  2. Наследники ALGOLа С/С++, ADA и вся эта линия.
  3. Прочие языки, например, специализированные языки; в частности, схлестнулись двое товарищей по поводу управления атомным реактором.

Из приводившихся аргументов считаю достойными упоминания вот что:

  1. Язык плох, если он не поддержан библиотеками (bootstrap'ing боком вылез )
  2. Язык лучше своего конкурента, если он допускает включение конкурента как подмножество.
  3. Язык весьма сильно зависит от компилятора.
  4. Язык и решаемая на нем задача неразрывны.

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

Однако критерий для сравнения должен был отыскаться, и мне хотелось, чтобы этот критерий был наиболее объективным - при всей иллюзорности самого понятия сравнения языков. Чтобы понять, что с чем сравнивать и какой результат сравнения считать лучшим, а какой худшим, я попытался рассмотреть процесс компиляции и исполнения программы вблизи. Способ выбрал тот, который показался мне самым надежным - попытаться написать компилятор с некоего гипотетического языка "Х" на некий гипотетический ассемблер некоей виртуальной машины "У". С последующим переходом на вполне реальный ассемблер i586, как наиболее доступный для РС.

В процессе решения этой задачи я столкнулся поочередно со следующими проблемами:

  • Для каких задач вообще создается язык?
  • Какие функции предоставить пользователю?
  • Какой синтаксис наиболее удобен для данной конкретной задачи?
  • Как представлять команды?
  • Будет ли язык компилируемым или интерпретируемым?
  • А почему именно таким?
  • Какую команду исполнять следующей за текущей?
  • Как осуществлять ветвления?
  • Как представлять данные?
  • Как найти требуемый элемент данных в памяти? (Какова должна быть форма адресации; структура хранения для быстрого поиска?)
  • Как обеспечить поддержку структур средним объемом 80-120 МВ?
  • Что делать, если команда или элемент данных не найдены?
  • Как поддерживать возможность включения и отключения части команд\данных без остановки системы? (Важно для сетевых приложений и вообще для надежности).
Эти вопросы кажутся очевидными мне сейчас; однако на их формулировку и постановку ушло немалое время. Естественно, изобретать велосипед не хотелось, и я ознакомился с различными описаниями языков и операционных систем, перечисленных ниже.

Языки: Pascal-Ada-Oberon-Modula-(2)(3), Object Pascal, Java, MUMPS (aka M), LISP-Sheme-CLISP-Flavor-AUTOLISP, Haskel, Forth, APL, Eiffel. UML, XML, Perl, Python, Tcl(for Tk), PHP (в весьма малой степени), HTML (куда уж без него), Simula, Objective-C, Smalltalk. Язык скриптов для игр- квестов. Наконец, С++, о котором мне известно больше всего.

OS: Unix-clones: Unix, Irix, QNX, Linux;

Others: OS\2, NextStep, BeOS, MSDOS, Win3.11.

Технологии: SOM, CORBA, Bonobo, WinAPI.

Возможно, эти описания не были исчерпывающими, и наверняка они не были очень тонкими. Также в процессе поиска и прочтения попадались упоминания о других языках, но, увы, без описаний. Наиболее удачными источниками информации, оказались книги Грейди Буча "Объектно- ориентированное программирование" (далее ссылка как 1) а также двухтомник Э.Хювенена и Й.Сеппянена "Введение в язык ЛИСП и функциональное программирование".(2) В процессе поглощения всей этой информации возникли выводы:

  • Данные делятся ТОЛЬКО на две группы:
    1. Символьные (Символ - последовательность знаков, представляющая что-то из моделируемого мира - (2, стр 61) ) .
    2. Числовые. (Число - символ, представляющий самого себя (2, стр 63) )
  • Любой подход не может быть равно эффективным в обработке чисел и символов.
  • Свойство (атрибут) - одно из фундаментальных понятий мира, поскольку присутствует в семействе LISP и в семействе Smalltalk, а эти языки трудно назвать близкими. То же самое можно сказать о понятиях "объект" и "связь".
  • Объем кода, скорость исполнения и суммарная эффективность системы зависят от того, насколько язык и процессор соответствуют друг другу. Говоря иначе, на символьном процессоре численные задачи будут выглядеть не лучше, чем на численном процессоре символьные задачи.

Однако, поскольку множество символов ВКЛЮЧАЕТ в себя числа как подмножество, символьные процессоры (теоретически!) могут лучше работать с числами, чем числовые процессоры с символами.

Отсюда довольно закономерное следствие: Операторные языки типа С со всеми клонами, куда я отношу и Pascal- Modula-Ada-Oberon, развивающиеся в направлении объектного моделирования мира, стремятся превратиться в символьные. "Объекты" объектно- ориентированых (далее ОО) языков есть попытка отобразить символьную обработку на числовые процессоры. Потому что числовые процессоры сейчас наиболее представлены на рынке.

Докажем это утверждение.

Во-первых, посмотрим на набор структур данных, вошедших в Standart Template Library языка С++. Это списки, вектора, хэш-таблицы, массивы, стеки, очереди. Очевидно, включено наиболее необходимое и часто используемое. Практически любую из этих структур можно реализовать на LISP'e со значительно меньшими затратами нажатий клавиш, чем на том же С++, (кроме битовых наборов). А список вообще является фундаментальным понятием ЛИСПа.

Во-вторых, рассмотрим итоговую структуру команд-данных, которая получается в ОЗУ машины на момент исполнения. Ее пытаются создать ВСЕ ОО- языки, и для описания ее же наперебой предлагают средства и фишки. (Кстати, именно за наличие или отсутствие упомянутых средств\фишек дамы и господа программисты клеймят либо возносят языки.) Согласно Грейди Бучу, структура описания задачи с точки зрения ОО-дизайна, есть дерево с допущением множественного наследования. Язык ЛИСП предполагает именно такую структуру данных в машинной памяти, только иначе называет описываемые в ней вещи. Понятие "объект" в ОО-языках, почти входит в определение ЛИСП- символа.

А в машинной памяти на момент исполнения с точки зрения компилятора и - главное! - процессора, символ ЛИСП-языков и объект ОО-языков представлены практически одинаково. Язык ЛИСП и его диалекты, ОО-языки и их диалекты в конечном итоге создают одну и ту же структуру в ОЗУ. Даже с учетом, что ЛИСП-процессоры все-таки чуствительно отличаются от обычных по архитектуре. Древовидная структура цепочек команд и блоков данных, образовавшаяся в памяти машины в результате применения любого из языков, выражена на языке команд конкретного процессора. (Далее такой язык условно называется ассемблером).

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

Способ этот имеет два достоинства: цифры в большинстве случаев вкладываются в стандартное количество символов, и правила их обработки просты. Поэтому правила можно не хранить вместе с цифрами, а зашить в обработчик. Что позволяет создавать быстрые переводчики пачки цифр в нужную форму путем изменения не всей структуры данных, а одного только обработчика. Если точнее - путем изменения набора правил интерпретации в обработчике. Недостаток же способа вот: отсутствует гибкость в описании сложных объектов - простые правила плохо справляются со сложными понятиями. Приходится брать массой, что иногда бывает дорого даже для очень быстрых процессоров. Вот почему на сегодняшней технике символьные языки типа LISP'a и (косвенно) Haskel'a, а также диалекты их, проигрывали и будут проигрывать операторным языкам в тех местах, где требуется большое количество цифр обмолотить за малое время.

Придуманный индустрией способ улучшить положение символьных языков - так переформулировать задачу, чтобы проблемная область предстала бы не сплошной массой данных, а некоторой УПОРЯДОЧЕННОЙ иерархией объектов, типа BSP/PVS-структур в 3D-шутерах. Такие иерархии понятий символьные языки обрабатывают лучше; сделать ошибку в иерархии также труднее, чем в сырой массе цифр. Именно на создание иерархий, упорядочение и классификацию сырых масс нацелены сейчас наиболее развитые ОО-языки, именно этому служит подавляющее большинство нововведений в этих языках.

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

Предугаданный читателем вывод: Фортран и Кобол - специальные языки, Фортран в математике, Кобол в экономике.

Несколько неожиданный вывод: С/С++ и операторные языки типа Pascal- Ada-Oberon-Modula линии - также специальные языки, описывающие специальную же область знаний - алгоритмическое представление данных. Отнесение их к универсальным языкам есть (само)сужение мышления до понятия: "универсальность - не более, чем хорошее представление алгоритмов".

LISP-Sheme и диалекты, Haskel и т.п. - специальные языки, описывающие область знаний как набор вычислимых элементов, т.н. функций, атомов и списков.

Специальные языки разных областей неправомерно сравнивать между собой. Поэтому вопрос, кто лучше, С++ или Haskel... продолжать?

Тогда что же такое универсальный язык? Универсальный язык - ассемблер. Он единственный позволяет описать любую, подчеркиваю, любую, область (вычилимых в принципе) данных максимально близко к тому процессору, который будет исполнять работу. Вот почему (а не из пальцовки, как некоторые "настоящие програмисты" говорят) весьма на пользу знание ассемблера: никогда не вредно удостовериться, что тебя правильно поняли, что переводчик (компилятор/ интерпретатор/ транслятор/ линкер ) не переврал твои соображения в исполнимом коде.

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

Откуда вывод: скорость и величина готового кода определяется тем, насколько хорошо язык описания проблемной области ложится на архитектуру. Следуя этой логике, лучшие языки/компиляторы те, кто генерирует маленькие быстродействующие программки. Что и пытались доказать джентльмены на linux.org.ru, приводя примеры кода и результаты компиляции и исполнения.

Но далеко не для всех задач важны скорость и малый объем. Некоторые задачи должны быть решены независимо от количества затраченного времени и занятых мегабайт. Наконец, малый объем кода далеко не показатель эффективности. Что больше: броузер или Сеть, которой он оперирует? Ведь никому не нужна Сеть, которую нельзя просмотреть, и никому не нужен броузер, которым нечего листать. По исполняемым функциям Сеть+броузер - один программный продукт. И что же, ценность данных в Сети как-либо зависит от броузера? И, наконец, эффективность - только один критерий из 10 канонических показателей качества программы. По значимости в общей оценке качества программы эффективность уступает и корректности, и надежности и полезности. (2 том 2 стр 129: "Корректность, надежность, полезность, эффективность, удобочитаемость, тестируемость, отлаживаемость, сопровождаемость, переносимость, адаптируемость" - в порядке источника.)

Учитывая вышесказаное, очень трудно написать гипотетический язык "Х" даже для четко определенного реального процессора 586 семейства, а не то чтобы для расплывчатой виртуальной машины "У". Большое количество рогаток и взаимоисключающих выборов всплывает в процессе создания даже игрушечного, учебного языка.

Для вышеупомянутой задачи по языку "Х" был принят следующий подход:

§ Область применения: язык позволяет описывать обычную пошаговую стратегическую игру, не весьма графическую, но с некоторыми отличиями от большинства. Должна быть возможность поддержки карт свыше 1000х1000, и одновременного использования 50 000 юнитов, (по мере емкости железа), с возможностью undo-отката, как в ACAD'e, например. (В натуре это, по-моему, только "Казаки" могут, да и то не в таких масштабах.)

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

§ Язык является интерпретируемым. Это позволяет:

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

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

  • А. Физические адреса вычисляются ДО исполнения. В процессе исполнения к ним обращаются без проверки их корректности. Экономит время и создает риск повалить систему, если по указанному физическому адресу ничего нет. Самый быстрый из известных способов. Малоприменим в условиях частого создания\удаления объектов.

  • Б. Способ А с созданием специального типа - "смарт-указателя". Этот указатель призван служить прослойкой между объектом-клиентом и объектом- сервером, и принимает на себя удар, если вдруг указуемый им объект-сервер несуществует или временно недоступен. В наших (непрофессиональных) кругах стал известен после распространения русского перевода книги Джеффа Элджера "С++ for real programmers". Способы А,Б применяются в С/С++ модели.

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

  • Г. Способ заключается в хранении символьного адреса объекта-параметра. Иерархия объектов - древовидная. Каждый раз, когда объект нужен, адрес разыменовывается, а полученный в реузльтате указатель проверяется на корректность. Если указатель некорректен, выполняется следующая команда или выполнение блока прерывается, по желанию пользователя. Это самый медленный способ, но он позволяет:
    • - не хранить никакой дополнительной информации о доступности объектов, (как в В)
    • - не описывать то, чего на этапе компиляции сам еще не знаешь, (как требуется в А, Б)
    • - иметь единый механизм защиты данных (проверка прав доступа того, кто пытается разыменовать любое имя),
    • - не заводить специального механизма и жесткого формата регистрации разнотиповых объектов (как это требуется в В),
    • - поддерживать выбор нескольких имен, указанных шаблоном (и делать это без просмотра всего списка имен),
    • - отключать и включать объекты в любое время.

Во втором пункте выбор был главным образом, между В и Г. Победил способ Г, перечислены его преимущества перед прочими четырьмя способами.

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

§ Язык является символьным, но допускает возможность описания таких структур данных, которые будут наиболее близкими к примитивным типам, то есть, допускает возможность вставок на числодробительных языках и поддерживает необходимые типы данных.

Потому что задача пошаговой игры с двумя подвохами сразу: описание юнитов, в особенности, алгоритмов поведения, сложное, а описание карты - большое. Символьные языки хорошо описывают сложные алгоритмы, но с трудом - большие количества числовых данных по карте, тем более по сколько-нибудь серьезной, типа 1000х1000. Приходится уповать на то, что выигрыш от продуманного разбиения модели игры на древовидный лабиринт данных превысит затраты по декодированию символов в числа. Второе, на что остается надеяться - на применение скоростей новых хардверных архитектур. Использование их взрывного нарастания мощности для реализации экспериментальных (пока) принципов и логики построения языка мне (субъективно, конечно) кажется куда более обоснованным, чем прямолинейное наращивание быстродействия языков уже существующих.

Не знаю, будет ли иметь применение такой язык, если даже и появится, но обдумывание вопросов его создания заставило меня понять довольно многое, не всегда приятное и на первый взгляд совсем для любительского уровня неочевидное. (Например, что С/С++ - не универсальные языки, хотя и близки к ним). И теперь трудно мне высказываться о сравнении языков: слишком многое нужно принять во внимание для корректности такого сопоставления. Ну и мнение о настоящести програмистов по их высказываниям также было составлено. Соответствующее. Одним из последствий этого мнения стало то, что я предлагаю этот текст в "Королевство", а не на linux.org.ru - если я в чем-то неправ (наверняка, т.к. специального программистского образования и опыта реальных проектов у меня нет), то здесь мне это объяснят. А на LOR просто наорут.

Пишите, очень интересно, что думает All всеведущий по данному поводу.
Заранее извиняюсь за возможную задержку ответов.

Михаил Бобров.

PS Привет Uno, хоть и давно не виделись.




Смотрите также материалы по темам:
[Средства разработки. Языки программирования.]

 Обсуждение материала [ 11-11-2005 15:12 ] 20 сообщений
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования