Микроэффективность
К наихудшим нарушениям хорошего стиля программирования относятся многочисленные «улучшения программы для повышения ее эффективности», которые можно найти во многих учебниках, стандартах организаций и головах программистов. Эти предложения не только усложняют чтение программы и снижают ее надежность, но и весьма слабо (если вообще хоть как-то) влияют на эффективность; отсюда и название раздела: «Микроэффективность».
Всякий, кто когда-либо серьезно занимался производительностью систем, знает, что эффективность достигается в результате тщательного анализа структур данных и алгоритмов, а также использования ресурсов, но не за счет таких тривиальностей, как исключение индексации, замена возведения в степень умножением, программирование на машинном языке или поиски самого быстрого способа обнуления регистра.
Центральная идея повышения производительности: сначала измерение и затем оптимизация макроэффективности. Можно значительно улучшить производительность, не занимаясь микроэффективностью.
Игнорируйте все предложения по повышению эффективности, пока программа не будет правильной. Худшее, что может быть сделано, – это начать беспокоиться о скорости программы до того, как она станет работать правильно. Быстрая, но неправильная программа бесполезна; медленная, но правильная всегда имеет некоторую ценность, а может оказаться и вполне удовлетворительной
Пусть оптимизирует компилятор. Многие из находящихся в эксплуатации компиляторов выполняют значительную работу по оптимизации: сокращение индексации, выявление выражений типа А**2, которые можно заменить одним умножением, вынесение постоянных выражений из циклов, размещение часто используемых переменных на быстрых регистрах и др. Программисту не нужно состязаться с компилятором; следует писать программы просто и ясно. Пусть об оптимизации заботится компилятор.
Не жертвуйте легкостью чтения ради эффективности. Как правило, предложения по улучшению микроэффективности сводятся к совокупности трюков и мешают достичь легкости восприятия.
Никогда не оптимизируйте, если в этом нет необходимости. Очевидно, что эффективность не является совсем уж несущественным вопросом. Однако программисту никогда не следует заниматься оптимизацией ради самой оптимизации; он должен оптимизировать только тогда, когда эффективность важна и только когда он знает точно, какая именно часть программы нуждается в оптимизации.
Добивайтесь эффективности за счет макроэффективности. В системе с виртуальной памятью можно значительно увеличить эффективность, располагая модули на странице так, чтобы минимизировать число страничных прерываний и размеры рабочего множества программы . Этот прием не требует изменений в тексте программы. К средствам повышения макроэффективности относятся также разумная организация ввода-вывода, выбор оптимальных алгоритмов и структур памяти. Например, некоторые алгоритмы сортировки и поиска в сотни раз быстрее других формально эквивалентных алгоритмов. Программисту не следует заботиться о микроэффективности, пока не будут исчерпаны все другие средства.
Добивайтесь эффективности на основе измерений, а не догадок. Доказано, что программисты крайне слабо угадывают причины неудовлетворительной эффективности программ. Это не результат каких-то недостатков самих программистов; из-за сложной природы программ и систем интуиция в вопросе об «узких» местах подводит почти всегда. Лучше всего при первоначальной разработке программы игнорировать большинство соображений, касающихся эффективности. Когда программа работает (и только в том случае, если ее эффективность неудовлетворительна), программист должен выполнить измерения, чтобы обнаружить и исправить те самые знаменитые «5 % программы, занимающие 90 % времени». Если программа была спроектирована правильно (это значит, что она легко адаптируема), такие изменения post factum должны быть несложными.
В сложных системах самые простые алгоритмы часто и самые быстрые. Многие современные ЭВМ имеют трехуровневую память: небольшой быстрый буфер между ЦП и основной памятью, сама основная память и виртуальная память, отображенная на устройства вторичной памяти. В таких системах локальность ссылок, т.е. отсутствие частых ссылок в широком диапазоне адресов программы и данных, – ключевой фактор эффективности. Это значит, что простые последовательные алгоритмы в таких условиях часто работают быстрее, чем более изощренные и сложные.