?

Log in

No account? Create an account
 
 
25 September 2009 @ 03:34 pm
Записки программиста.  

Интересные просчеты в программировании - 1.

Отступление 2.
Мне всегда хотелось составить что-то вроде классификации ошибок, но никогда не удавалось
сосредоточиться на этом. Может в процессе написания этих заметок я чуть-чуть продвинусь в этом
направлении.

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

Надо сказать, что на самом высоком уровне я бы выделил два класса ошибок:
1. Ошибки вызванные незнанием или недостатком опыта (и недостаточным или неудачно
использаванным временем, потраченным на придумывание как это нужно сделать правильно
и хорошо).
2. Использование расхожих правил и шаблонов без достачного осмысления их, задачи и разумности их
применения к задаче. Такие просчеты иногда напоминают мне некоторые традиции ремесла
(учат "делай так").

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

Итак, постараюсь привести примеры.

1. Макаронный код.

Наиболее страшное впечетление произвел на меня код ядра супервизора операционной системы
"J level DOS" для машин "ICL System 4". Это было начало 70-х, я тогда работал в ИАТ-е (потом
переименованном в ИПУ) и у нас была ICL 4-70. Мы имели мсходные тексты системы и непрерывно
занимались как отлавливанием ошибок, так и разными усовершенствованиями. Впричем, исходных
кодов ядра не иметь было невозможно: установка системы (ито называлось генерацией) включало
в себя трансляцию всего ядра с языка ассемблера (он назывался usercode) с предварительно
определенными глобальными макро-переменными и заданными щперандами макрокоманд.
Распечатанный дистинг ядра в сложенном гармошкой виде имел толщину в несколько сантиметров.
И значительная часть его представляла собой сплошную линейку кода с передачами управления.
Представляете себе, каково было это читать (или исправлять). Линии, нарисованные карандашем,
ведущие от команды передачи управления к месту, куда управление передавалось помогали только, если
расстояние было небольшим...

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

Главным недостатком такого кода, конечно, является не столько "макаронность", сколько монолитность:
в коде не разделенном на не очень большие компоненты, функционально самостоятельные и с ограниченными
связями можно голову сломать: сложность растет лавинно.

2. "Ручная трансляция". Это было одной из первых реализаций языка Рефал (те же 70-е), работа (как и сам язык),
которую я щитаю одной из выдающихся. Текст на исходном языке транслировался на промежуточный код
транслятором, написанным на рефале (хотя был и ручной, раскруточный, вариант, бес оптимизаций).
А интерпретатор был написан на ассемблере.

Вся информация представлялась в виде двухстороннего списка с несколькими видами элементов, некоторые
из которых содержали также ссылки на другие элементы.

И работа с элементами списков было запрограммирована на ~95% как последовательность операторов
типа "сдвинуться по списку" вправо или влева, удалить или вставить что-то в список, перешить кусок списка
из одного места в другое и подобных; всего было пара десятков таких операторов или чуть больше.
Отдельные функции для каждого из таких операторов было использовать слишком дорого: каждый
занимал типочно несколько команд и передача параметров была бы не тривиальной. Поэтому они были
закодированы в ручную (для меня, при моей лени, такое решение было бы невозможным :) ).

Надо сказать, что авторы этого кода были людьми весьма способными и яркими, просто тогда они
имели не так много опыта и об использовании макрокоманд не знали.

И все бы ничего, но все это было запрограммировано на одну из первых машин серии ЕС ЭВМ с маленькой
памятью и для представления ссылок использовались 2-х байтные поля. То есть, не больше 64К памяти.
А хотелось использовать рафал на более современных машинах.

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

А определить макры, умеющие работать с разными представлениями ссылок (пойнтеров) было уже совсем
легко. Это был первый рефал транслятор работающий на "больших" машинах, который мы немало использовали
в достаточно "промышленном" режиме. Помню, ка меня потом осторожно спрашивали разрешения переслать
его в США, автору Рефала В. Ф. Турчину, обещая предварительно проверить у удалить все упоминания
моего имень из кода и комментариев :) .