Полет по приборам
Часто поспешные заключения о том, какие части программы являются важнейшими, ошибочны, так как опыт программистов, использующих средства анализа, говорит о том, что их интуитивный вывод был ложным.Дональд Кнут, Структурное программирование с операторами go to
Самое страшное в обучении летному делу — полет по приборам. Есть целый ряд ситуаций, где ваши глаза, вестибулярный аппарат, ваше представление о том, как обстоят дела, обманывают и потом убивают вас. Люди вообще не очень хороши в ориентировании «на глаз» в трехмерном пространстве. Даже пилоты. Самое тяжелое в этом деле — не просто научиться использовать приборы, а научиться им доверять. Вы просто должны признаться сами себе, что возможности человека ограничены, каким бы талантливым он ни был.
Работа над производительностью чем-то похожа на полет по приборам. Люди не могут предсказывать работу сложных компьютерных систем. Даже программисты. Особенно программисты. Наша возможность создавать огромные и сложные структуры ошибочно позволяет нам думать, что мы в состоянии постичь их до конца. Я называю это Комплексом Творца и это наша первая профессиональная болезнь. Очень талантливые программисты иногда пытаются оптимизировать приложение без нужных для этого исходных данных и незамедлительно сваливаются с небес на землю.
То, как программа работает и как она исполняется — абсолютно разные вещи. Если вы пишете программу, то, конечно же, знаете, как она работает. Вы можете описать ее поведение, логическую последовательность действий и сложность вычислений. По большому счету, программирование — это игра в компьютер в голове. Это, своего рода, наша фишка. Несомненно, нужно использовать свой мозг и свой опыт в работе. Но наши расчеты далеко не всегда верны. Никогда не забывайте, что человеческие представления о том, что происходит, — всегда всего лишь грубая модель происходящего в реальном мире, на реальном компьютере, с реальными пользователями и данными.
Большое О
Вы наверняка помните этот график со школьной скамьи. Он иллюстрирует значительную разницу во времени выполнения вычислительных команд разного уровня сложности. O(N²)
настолько превосходит O(N)
, что коэффициенты не играют никакой роли. Это основополагающий принцип. Без комплексного анализа мы столкнемся с использованием гораздо большего количества циклов работы ЦП.
Однако, в то время как этот график иллюстрирует истинное утверждение, он также таит в себе и ложное. Глядя на этот график, легко прийти к выводу, что комплексный анализ — это все, что вам нужно знать о производительности, а все остальное не выходит за рамки погрешности.
В реальной жизни сложность большого О почти никогда не является причиной медленной работы вашей программы. Посмотрите на наклон кривой N
в квадрате. Как видно, она либо настолько незначительна, что вообще не оказывает никакого влияния на программу, либо так велика и ее воздействие настолько очевидно, что программы с такого рода ошибками исправляются довольно быстро.
Выходит, что вам приходится бороться с тем, что вас учили игнорировать: коэффициенты, постоянные коэффициенты, варьируемость. На бумаге преобразовать O(N²)
в O(N)
очень просто. Такие алгоритмы достаточно легки и просто поддаются изучению. Но как в настоящей, уже существующей программе, вы преобразуете 2N
в N
? Откуда вам знать, какой именно там коэффициент вообще? (Насколько вообще велика эта гора? По силам ли вам ее убрать?)
Так, Комплекс Творца снова дает о себе знать. Почему бы просто не проанализировать программу глубже? Я же ведь умный, а коэффициенты по своей сути незначительны. Одна только загвоздка у нас с таким подходом — вам нужно будет исключить все аспекты, влияющие на систему: язык, компилятор, операционную систему и оборудования, на котором она стоит, все данные и еще много чего другого.
Представьте себе два абсолютно идентичных компьютера с одним и тем же набором программного обеспечения, настройками и спецификацией оборудования. Они отличаются только оперативной памятью, — на одном стоит чип с 4 гигабайтами, а на другом с 16-тью. В большинстве, не всегда, но в большинстве случаев их результаты будут значительно отличаться. Даже если вы можете понять, почему так происходит, ваши догадки так же эффективны, как и мои, — то есть, никак. Есть еще множество других аспектов, которые могут влиять на систему.
Чем более подробной вы делает модель своей программы, тем больше она начинает походить на медленную, с кучей ошибок, неполноценную симуляцию этой программы. Все предположения — это симуляция. Это не что иное, как результат Проблемы Остановки. То, что подразумевает Равенства Тюринга. А также ответ на вопрос, почему, чтобы быть хорошим программистом, надо уметь хорошо симулировать компьютер. Но угадайте, кто лучше всех умеет симулировать компьютер?
Другими словами, не мучайте себя. Доверяйте вашим приборам. Делать предположения, несомненно, хорошо (бороться с вашим внутренним комплексом бога и тренировать интуицию), но реальность должна быть судом в последней инстанции. Если вы хотите узнать, как ведет себя та или иная программа, то просто запустите на компьютере эту чертову штуку и посмотрите, что получится. «Я не знаю, а ну-ка проверим» — ответ на все ваши вопросы.
Даже если у вас есть наработки, хороший опыт и устоявшиеся привычки. Легко потерять рассудок, глядя на числа, которые подтверждают ваши догадки. Это также легко, как не заметить что-то, что не соответствует вашим ожиданиям. Это все очень порочные и распространенные комплексы. Не ищите доказательств ваших теорий. Если ваша теория имеет место быть, то она пройдет все тесты очень просто. Если нет, то вы увидите это еще быстрее. Ошибаться — это прерогатива науки.
Ваша задача — не переплюнуть компьютер. Ваша цель — сделать все правильно. Ваша цель — найти истину. Представляйте себе это как оптимизацию во имя правды.