Кодеры за работой. Размышления о ремесле программиста - Питер Сейбел 28 стр.


Армстронг: Да, но почему - не знаю. Любопытно, что если двум программистам дать одну и ту же задачу - конечно, смотря какую, я говорю о задачах скорее математического свойства, - то они с большой вероятностью напишут одинаковый код. Если произвести форматирование, переименовать переменные и функции, получится одно и то же - одинаковые алгоритмы. Мы создаем что-то - или просто снимаем покрывало? Это как со статуей. Мы снимаем покрывало и обнаруживаем, что алгоритм всегда был там. Изобретаем ли мы новый алгоритм или уже существующую структуру? С некоторыми алгоритмами, в основном математическими, такое случается. Но, скажем, если я занимаюсь реализацией протокола для телефонии, такого чувства нет. Нет ощущения, что я снимаю покрывало со статуи.

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

Армстронг: Да. Это вроде фэн-шуй. Я люблю компактный код, хорошо сбалансированный и структурированный. Удаляешь одно, другое, и настает момент, когда удалить больше ничего нельзя - программа не будет работать. Вот в этот момент она прекрасна. Любое возможное изменение может ухудшить алгоритм, но сейчас он прекрасен.

Сейбел: Вы говорили о том, как с Робертом Вирдингом пересылали друг другу куски кода. Как каждый из вас изменял низкоуровневые детали форматирования, по поводу которых программисты без конца спорят?

Армстронг: Они не затрагивают красоту алгоритма.

Сейбел: Но это часть эстетики, это вкус.

Армстронг: Да, но я не назову код уродливым, потому что в нем есть пробел после запятой. Уродливое - это линейный поиск, когда можно применить деление пополам, или логарифмический метод. Делайте линейный код, если у вас список из десяти элементов, - пожалуйста! Но при больших структурах данных применяйте бинарный поиск. В линейном виде это выходит некрасиво. Математические алгоритмы - как платоновские идеальные сущности. Что-то близкое к архитектуре. Вы любуетесь красивым зданием, которое не есть математический объект. Это не шар, не сфера, не призма - это небоскреб. Но выглядит он великолепно.

Сейбел: Что делает человека хорошим программистом? Нанимая на работу программиста, на что вы смотрите?

Армстронг: Как человек выбирает задачи. Что для него важнее - задача или решение? Мне симпатичнее человек, который говорит: "У меня была вот такая занятная задача". Вы спрашиваете, в каком самом интересном проекте он участвовал, просите показать соответствующий код, спрашиваете, как он решал задачу. Для меня не столь важно, хорошо ли соискатель знает язык А или язык Б. Обычно программисты всеми языками владеют одинаково хорошо - или плохо. Хороший Си-программист будет успешно работать и с Erlang, это очень надежный показатель. Есть, конечно, исключения, но умственные навыки, необходимые для работы с одним языком, можно применить и к другим.

Сейбел: В некоторых компаниях соискателям дают решать логические задачи. Вы так делаете?

Армстронг: Нет. Есть очень хорошие программисты, которые в них соображают туго. Помню одного из парней, работавших с Erlang, - у него была степень по математике, и он напоминал алмазный бур, прогрызающий гранит. Как-то раз он загрипповал и взял домой листинги из Erlang. Потом он пришел, добавил в код атомарное выражение и сказал: "Это поставит эмулятор на замкнутый цикл". Он обнаружил, что изначальное хеш-значение этого выражения равно нулю, и взял какой-то модуль для перехода к следующему выражению - оно тоже оказалось равным нулю. И он декомпилировал хеш-алгоритм для одного патологического случая. Он даже не запускал программы - посмотреть, как они работают: он читал их. Но медленно. Довольно медленно. Не знаю, как бы он решил по-быстрому логическую задачу.

Сейбел: Есть ли еще навыки, необходимые хорошему программисту?

Армстронг: Где-то я читал, что нужно иметь хорошую память. Думаю, это так.

Сейбел: Билл Гейтс однажды сказал, что до сих пор мог бы выйти к доске и написать большие фрагменты кода на Бейсике, который писал для Altair, хотя прошло уже лет десять. А вы можете вот так вспомнить свой старый код?

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

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

Я не беспокоюсь о потере кода - если шаблоны в голове, я все вспомню. Даже не столько вспомню, сколько сделаю заново. Не думаю, что это припоминание, скорее воссоздание. Если Билл может вспомнить именно сам текст, то я - нет. Но, конечно, я долго помню структуру кода.

Сейбел: Скажите, обмен сообщениями в духе Erlang поможет раз и навсегда решить проблему параллельного программирования?

Армстронг: Нет, конечно. Это всего лишь усовершенствование, пусть и большое, по сравнению с разделяемой памятью. Думаю, Erlang выполнил, по крайней мере, одну задачу - продемонстрировал это. Работая над Erlang, мы всегда говорили на конференциях: "Надо будет копировать все данные". И слушатели, кажется, воспринимали наши доводы насчет безотказной работы системы - копирование всех данных вводилось для этого. Нам возражали: "Но ведь тогда все будет работать страшно медленно", - а мы отвечали: "Зато безотказно".

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

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

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

Есть параллелизм данных; это не настоящий параллелизм, скорее он связан с процессами в кэше. Если вы хотите, чтобы программа на Си выполнялась эффективно, и если *р находится на 16-байтовой границе, то при доступе к *р доступ к *(р+1) практически бесплатен, поскольку строка кэша вытягивает его. Вам надо знать, насколько широки строки кэша, сколько байтов вы сможете утянуть при одном перемещении в кэш? Этот вид параллелизма можно успешно использовать, если быть очень осторожными со структурами данных и точно знать, где что находится в памяти. Все запутанно, и разбираться с этим не очень охота.

И наконец, многоядерные процессоры. К концу десятилетия они будут 32-ядерные, а к 2019 году ядер будут миллионы. Поэтому вам нужно взять параллельные участки своей программы и отобразить на ядра компьютера. Согласен, это довольно громоздкая операция. Вычисление начинается в другом ядре, из него потом приходит ответ - это требует времени. Если надо просто сложить два числа, это не стоит усилий - больше времени потратится на перенос данных между ядрами, чем на то, чтобы сделать все на месте.

Erlang неплохо приспособлен для этого. Программист говорит: "Мне нужен процесс, и еще, и еще". Процессы распределяются между ядрами. Может быть, стоит подумать над их физическим распределением между ядрами. Не исключено, что процесс, порождающий другой процесс, обменивается с ним данными. Имеет смысл поместить его в то ядро, которое ближе. Если же обмен данными не очень интенсивен, то можно поместить и подальше. Процессы, занимающиеся вводом/выводом, стоило бы расположить у края чипа, с другими процессами, занимающимися вводом/выводом. Чипы становятся все больше, и надо задуматься над тем, что размещение данных в середине чипа обходится дороже, чем на краю. Если есть три сервера и база данных, то ее мы поместим в середину, а серверы, которые общаются с клиентами, на край. Но все это подлежит исследованию.

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

Армстронг: Конечно, идея. Меня спрашивают: "Что будет с Erlang? Станет ли он популярным языком?" Этого я не знаю. Думаю, он уже стал важным языком. Он может повторить судьбу Smalltalk - очень важного языка, который имел горячих сторонников, но широко не применялся. С Erlang может произойти то же самое. Возможно, заложенные в нем идеи придут к миллионам пользователей, если их реализует Microsoft в Common Language Runtime, добавив там-сям фигурных скобок.

7. Саймон Пейтон-Джонс

В 1987 году он стал одним из инициаторов проекта, в результате которого появился язык программирования Haskell. Сегодня Саймон Пейтон-Джонс - ведущий исследователь лаборатории Microsoft Research, находящейся в британском Кембридже. В 1998 году он выпустил переработанное описание языка Haskell, являющееся текущим стабильным описанием. Кроме того, Пейтон-Джонс - ведущий разработчик Glasgow Haskell Compiler (GHC), "стандартного компилятора де-факто", согласно сайту haskell.org. Он же снабдил язык часто приводимым официальным девизом "Избегать успеха любой ценой".

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

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

Сейбел: Когда вы научились программировать?

Пейтон-Джонс: В школе. Intel только что выпустила 4004 - первый в мире микропроцессор. Но у нас ни 4004, ни чего-нибудь похожего не было - любители в то время могли достать его лишь с большим трудом. Был только школьный компьютер IBM - странная машина, собранная из каких-то запчастей. Системы постоянного хранения данных не было, и программу всякий раз приходилось набирать с нуля.

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

Освоив немного эту машину, мы выяснили, что в Суиндонском техническом колледже есть компьютер. Мы стали ездить туда в один из выходных на невероятно медленном автобусе - поездка занимала около часа. То был Elliot 803; он помещался в особой комнате в полудюжине белых шкафов размером с холодильник, и управляла им женщина-оператор в белом халате.

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

Сейбел: В школе ведь не было никаких уроков.

Пейтон-Джонс: Конечно, нет! Совсем ничего, никаких компьютеров не было в программе.

Сейбел: То есть просто - "Эй, ребятки, вот вам компьютер, действуйте".

Пейтон-Джонс: Именно так. Он помещался в большом шкафу, обычно закрытом. Но мы брали ключ, включали его - и вот вам дисплей, показывающий только то, что есть в регистрах, и десятичные номера - содержимое ячеек памяти. Вводишь программу, нажимаешь Go. И можно было пройти ее пошагово. Не было даже языка ассемблера за отсутствием ASCII-символов. В буквальном смысле слова машинный код, отображаемый при помощи десятичных чисел, даже не шестнадцатеричных.

Сейбел: Но экран-то был?

Пейтон-Джонс: Телевизионный экран. Единственное средство вывода информации.

Сейбел: А для ввода?

Пейтон-Джонс: Нечто вроде сенсорной клавиатуры, никаких механических кнопок - довольно сложное устройство. Надо было только коснуться кнопки. Всего их было десятка два.

Сейбел: Только для чисел?

Пейтон-Джонс: Да, и еще кнопки Go и Step. И еще - "показать данную ячейку памяти". Все крайне примитивно, но оттого раззадоривало еще больше.

Сейбел: Раз так, выходит, программу надо было продумывать заранее, в мелочах, до того как вводить ее в машину.

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

Затем я поступил в Кембриджский университет; эра микропроцессоров только начиналась. В университете был компьютерный клуб. Там стояла большая машина под названием Phoenix с исключительно замысловатой системой учета ресурсов.

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

И мы с девяти вечера до трех ночи торчали там, сочиняя программы. На чем мы писали? На BCPL. Опять сплошная самодеятельность, как видите. В то время я занимался математикой и формально совсем не учился компьютерным наукам.

Тогда, в 1976-1979 годах, не было степени бакалавра как таковой. В последний год можно было выбрать специализацию, например компьютерные науки. Но нельзя было заниматься одним предметом все три года - надо было еще изучать, скажем, математику, естественные науки. Я занимался математикой и закончил со специальностью "Науки об электричестве". Тогда я считал, что компьютеры - так, игрушка, а специализация должна быть серьезной.

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

Сейбел: "Науки об электричестве" - это то, что в Америке называется "Электротехника"?

Пейтон-Джонс: Совершенно верно. Мой школьный приятель Томас Кларк тоже учился в Кембридже. Мы с ним собирали компьютеры. Покупаешь микропроцессор, много транзисторов серии 7400 и соединяешь их проводами. Большой проблемой оставались принтеры и экраны. С ними было трудно.

Сейбел: Стоили очень дорого.

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

Сейбел: Значит, в 1976-1979 годах вы собирали компьютеры из подручных средств. Но ведь как раз в это время выпустили Altair.

Пейтон-Джонс: Да. Самоделки уже выходили из моды. Но нам просто нравилось все это.

С этими нашими машинами была еще одна сложность - программы. Самое продвинутое, что можно было загрузить, - конвеевская игра "Life" (Жизнь). Она работала прекрасно. Но что-то серьезное, вроде языка программирования, требовало слишком много работы - у нас были крохотные постоянные хранилища данных. Ну и, кроме того, все писалось в шестнадцатеричном коде, никакого ассемблера.

Сейбел: Машинный код в чистом виде.

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

Сейбел: Помните вашу первую интересную программу?

Назад Дальше