Лебедь, рак и щука, или Гадкий утенок

We use cookies. Read the Privacy and Cookie Policy

Лебедь, рак и щука, или Гадкий утенок

Мой коллега и товарищ, Саша Кротов, вдвоем пару с которым мы, собственно, и сделали практически всю работу, имеет прекрасное образование (по моим наблюдениям, выпускники мехмата зачастую имеют более высокую программистскую квалификацию, чем окончившие ВМК?—?да простят меня мои однокашники!). Несмотря на естественное для его возраста отсутствие опыта крупных разработок, он поразительно быстро "въехал" в проект и вообще в проблемную область и очень скоро стал совершенно равноправным его участником. К этому времени он вполне осознал, насколько интереснее программировать компиляторы, чем наполнять базы данных, рисовать на экране вертящиеся фигуры или писать байты в порт и ожидать их оттуда.

Третий участник проекта был (и есть) настолько талантлив как программист, что мог с одинаковым успехом участвовать, кажется, решительно в любом программном проекте. Базы данных и сети, протоколы обменов и многозадачность, графика и издательские системы, вычислительные алгоритмы, распределенная обработка и искусственный интеллект?—?во всем он чувствовал себя уверенно и имел на то полное основание.

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

В компиляторе есть проектные ошибки (хотя к настоящему моменту большинство из них мы извели, поначалу их было немало). "Снаружи" эти ошибки практически никак не проявляются и не дают покоя только нам, знающим наизусть все его внутренности. Некоторые из них были просто неизбежны, так как, закладывая то или иное решение несколько лет назад, мы никак не могли представить, к чему приведет непредсказуемая эволюция языка. Другие ошибки объяснялись недостаточным опытом?—?практически впервые мы пытались делать то, что называется коммерческим программным продуктом и исходили только из академических представлений о том, какова должна быть архитектура компилятора.

Но самая неприятная категория проектных ошибок?—?это те, которые возникли из-за недостаточно тщательного анализа на начальных этапах проекта и, что хуже, из-за того, что по некоторым принципиальным вопросам имелись различные мнения. Принимать решение всегда сложно еще и потому, что чье-то мнение, как правило, приходится отвергать. Тяжело и тому, кто отвергает, и неприятно тому, чье мнение не учитывается. Зачастую бывает так, что трудно предпочесть какой-либо конкретный вариант из нескольких альтернатив просто потому, что все они достаточно обоснованы и могут быть использованы; в таких случаях необходимо чье-то волевое решение, которое все участники должны безоговорочно принять. У нас в свое время просто не хватило духу проговорить все до конца и определиться полностью по всем принципиальным вопросам. В результате некоторые существенные решения принимались "по умолчанию" тем или иным участником проекта без согласования с другими. Винить в этом, естественно, следует прежде всего старшего участника?—?автора этой статьи (как самого опытного, а не самого умного!).

Так, компилятор сначала выполняет полный семантический анализ всего исходного текста, и только потом генерирует для всей программы результирующий код. Почему такая организация компилятора была выбрана третьим участником, до сих пор непонятно. Какое-то объяснение было тогда дано, но оно тут же выпало из нашей памяти, и вспомнить сейчас невозможно, а попытаться самим объяснить?—?не получается. Такое решение (удивительно, но принятое без всякого обсуждения) приводит к тому, что компилятор сохраняет полное дерево программы (и, следовательно, вынужден сохранять и семантические таблицы, так как они друг с другом сильно связаны) вплоть до завершения обработки всего исходного текста. Более логичным и экономичным был бы подход, согласно которому для каждой функции выполняется вся обработка, вплоть до генерации кода, после чего в структурах компилятора сохраняется только информация из ее заголовка, необходимая для компиляции вызовов. Исключение достаточно сделать для встраиваемых (inline) функций, да и то не всегда.

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

Получилось так, что семантические таблицы были спроектированы, имея в виду второй, более естественный подход, а структура дерева?—?согласно первому подходу. Разработчику семантических таблиц (мне, попросту говоря), будучи поставленному перед фактом уже в процессе реализации, ничего не оставалось, как срочно перекроить их структуру. Крайне неприятно, но эта ситуация сохранилась и по сей день. Надо ли уточнять, что эти структуры были в свое время придуманы двумя участниками, которые в свое время не смогли (не захотели) вместе обсудить свои решения и возможные проблемы…

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

Комментарий 2001 года

Подобных, более мелких, но крайне неприятных рассогласований и неувязок было много, и, самое ужасное, с течением времени их число нарастало. Возникало тяжелое ощущение того, что компилятор?—?это большая темная комната, а у тебя только маломощный фонарик, который в состоянии осветить небольшой аппарат?—?твои модули. От аппарата тянутся в темноту провода и вереницы зубчатых колес. Что делается в дальних углах, неизвестно. Иногда вокруг раздаются какие-то звуки, из темноты выступают части каких-то движущихся механизмов, назначение которых остается неведомым, даже если осветить их. Время от времени из темноты раздается голос, настоятельно требующий: "нажми на кнопку с надписью ABC", "переведи рычаг XYZ в правое положение". Что делается в комнате и как все работает вместе, понять совершенно невозможно.

Пришло время говорить о неприятном?—?через некоторое время от нас ушел третий участник. Он весь был ориентирован на получение результата, а не на процесс его достижения. Само по себе это исключительно ценное качество, его наличие (подкрепленное высокой квалификацией) гарантирует успешное завершение работы в заданные сроки. Однако в данном случае оно обернулось своей худшей стороной?—?откровенно небрежным кодированием ("компилятор соптимизирует"?—?классический ответ на все замечания), принятием важных решений "на ходу", без всякого обсуждения и плохо скрываемым недовольством коллегами, которые непонятно почему копаются там, где надо скорее программировать. Главное?—?скорее! За один день сделать работоспособный синтаксис, за месяц?—?добиться трансляции программы "Hello world!". Сложность системы не играет никакой роли, все программы устроены одинаково. Модули должны взаимодействовать согласно своим интерфейсам, обсуждать и комментировать которые нет смысла, они и так сами за себя говорят?—?на то они и интерфейсы.

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

Скрытое напряжение в команде возрастало, и первым не выдержал тот, кто не связывал с проектом все свои помыслы. В один прекрасный день мы двое обнаружили в общем каталоге с рабочими тестами полторы сотни примеров, которые ломали компилятор, причем ломали его вроде бы на тех модулях, которые писали мы. Третий участник исчез. Мы поняли это однозначно: мое терпение кончилось, разберитесь, наконец, с тем, что у вас не работает, догоните меня, а я пока займусь другими делами.

Ошибки были исправлены примерно за неделю (половина из них оказалась "не нашими", а как раз того третьего), однако он так и не вернулся в проект никогда… Мы остались вдвоем.

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

Я хочу, чтобы нас правильно поняли. У нас нет к ушедшему абсолютно никаких претензий. Нас не обманули, не предали, не нарушили никаких обязательств. Более того, я вполне допускаю, что сами мы не без греха, и работа в то время шла не слишком ритмично (надеюсь, что и ему уход не принес много горечи). И если я рассказываю об этом эпизоде, то только потому, что мы сами многое при этом поняли и многому научились.

Чем меньше коллектив, тем большее, часто определяющее, значение приобретает проблема личностной совместимости?—?характеров, темпераментов, привычек и манер, т. е. вещей, которые прямо не относятся к профессии. Примером, близким к идеалу, можно считать Дениса Ритчи и Кена Томпсона. Вот как последний говорил об этом в выступлении при вручении ему премии имени Тьюринга: "Наше сотрудничество было образцом совершенства. За десять лет, которые мы проработали вместе, я могу вспомнить только один случай нескоординированной работы. Тогда я обнаружил, что мы оба написали одинаковую ассемблерную программу из 20 строк. Я сравнил наши тексты и был поражен, обнаружив, что они совпадают посимвольно. Результат нашей работы был намного больше, чем вклад нас обоих по отдельности". Но это, как говорится, от Бога, один случай на миллион. Каких-либо рекомендаций давать невозможно, единственное?—?надо быть очень и очень осторожным при формировании коллектива.

Что же касается тщательного проектирования и особенностей процесса реализации, то изначальное жесткое разбиение на модули, снабжаемые строгими спецификациями, после чего реализацию этих модулей можно отдать даже и студентам, проходит для хорошо формализуемых и не впервые решаемых типовых задач, а не для систем с предельно сложной логикой, где решительно все взаимосвязано. Традиционная этапность разработки ПО (спецификация и анализ требований, проектирование архитектуры системы, спецификация модулей, реализация и т. д.) в данном случае неизбежно размывается, модифицируется и приобретает существенно итеративный характер: проектирование (и перепроектирование) многих структур данных и алгоритмов компилятора происходит неоднократно уже на этапе реализации. Такой возвратно-поступательный процесс, как мне кажется, органически характерен для создания любой сложной программной системы, семантика которой не может быть осознана и формализована полностью на этапе проектирования в приемлемые сроки. К тому же надо иметь в виду, что в процессе работы над компилятором изменялся и сам язык?—?процесс стандартизации зачастую преподносил совершенно неожиданные сюрпризы, и многого нельзя было предугадать заранее.

Эта точка зрения, точнее, конкретный опыт, быть может, входит в противоречие с современными моделями процесса создания ПО, описанными классиками,-- Г.Бучем, Э.Йоданом и другими, однако повторю еще раз, компилятор Си++?—?не вполне типичная программная система, по крайней мере, с точки зрения семантической и логической сложности.

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

А вы?

Данный текст является ознакомительным фрагментом.