Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 868
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
580
ЧАСТЬ V Усовершенствование кода водительности. Если оптимизация выполняется после создания полной систе- мы, разработчики могут определить все проблемные области и их относитель- ную важность, что способствует эффективному распределению времени.
쐽
Концентрация на оптимизации во время первоначальной разработки отвле- кает от достижения других целей. Разработчики погружаются в анализ алго- ритмов и сокровенные дискуссии, от которых пользователям ни тепло, ни хо- лодно. Корректность, сокрытие информации, удобочитаемость и т. д. стано- вятся вторичными целями, хотя потом улучшить их сложнее, чем производи- тельность. Работа над повышением производительности после создания пол- ной программы обычно затрагивает менее 5% кода. Что легче: повысить про- изводительность 5% кода или улучшить удобочитаемость всего кода?
Короче говоря, главный недостаток преждевременной оптимизации — отсутствие перспективы. Это сказывается на быстродействии итогового кода, других, еще более важных атрибутах производительности и качестве программы, ну а расплачиваться за это в итоге приходится пользователям. Если время, сэкономленное благодаря реализации наиболее простой программы, посвятить ее последующей оптимиза- ции, итоговая программа непременно будет работать быстрее, чем программа,
разработанная с использованием неорганизованного подхода к оптимизации
(Stevens, 1981).
Иногда оптимизация программы после ее написания не позволяет достичь нуж- ных показателей производительности, из-за чего приходится вносить крупные изменения в завершенный код. Можете утешить себя тем, что в этих случаях оп- тимизация небольших фрагментов все равно не привела бы к нужным результа- там. Проблема в таких ситуациях объясняется не низким качеством кода, а неадек- ватной архитектурой программы.
Если оптимизацию нужно выполнять до создания полной программы, сведите риск к минимуму, интегрировав в процесс оптимизации перспективу. Один из спосо- бов сделать это — задать целевые показатели объема и быстродействия отдель- ных функций и провести оптимизацию кода по мере его написания, направлен- ную на достижение этих показателей. Определив такие цели в спецификации, вы сможете следить сразу и за лесом, и за конкретными деревьями.
Быстродействие программы не менее важно, чем ее
корректность — НЕВЕРНО! Едва ли можно представить ситуацию, когда программу прежде всего нужно сделать быстрой или компактной и только потом корректной. Дже- ральд Вайнберг рассказывает историю о программисте, ко- торого вызвали в Детройт, чтобы он помог отладить нера- ботоспособную программу. Через несколько дней разработчики пришли к выво- ду, что ситуация безнадежна.
На пути домой он обдумывал проблему и внезапно понял ее суть. К концу полета у него уже был набросок нового кода. В течение нескольких дней программист тестировал код и уже собирался вернуться в Детройт, но тут получил телеграмму,
в которой утверждалось, что работа над проектом прекращена из-за невозмож- ности написания программы. И все же он снова прилетел в Детройт и убедил руководителей в том, что проект можно было довести до конца.
Дополнительные сведения Опи- сания других занимательных и поучительных случаев можно найти в книге Джеральда Вай- нберга «Psychology of Computer
Programming» (Weinberg, 1998).
ГЛАВА 25 Стратегии оптимизации кода
581
Далее он должен был убедить в этом участников проекта. Они выслушали его, и когда он закончил, создатель старой системы спросил:
— И как быстро выполняется ваша программа?
— Ну, в среднем она обрабатывает каждый набор введенных данных примерно за
10 секунд.
— Ага! Но моей программе для этого требуется только 1 секунда.
Ветеран откинулся назад, удовлетворенный тем, что он приструнил выскочку.
Другие программисты, похоже, согласились с ним, но новичок не смутился.
— Да, но ваша программа
не работает. Если бы моя не обязана была работать, я мог бы сделать так, чтобы она обрабатывала ввод почти мгновенно.
В некоторых проектах быстродействие или компактность кода действительно имеет большое значение. Однако таких проектов немного — гораздо меньше, чем ка- жется большинству людей, — и их число постоянно сокращается. В этих проек- тах проблемы с производительностью нужно решать путем предварительного проектирования. В остальных случаях ранняя оптимизация представляет серьез- ную угрозу для общего качества ПО,
включая производительность.
Когда выполнять оптимизацию?
Создайте высококачественный проект. Следите за правиль- ностью программы. Сделайте ее модульной и изменяемой,
чтобы позднее над ней было легко работать. Написав кор- ректную программу, оцените ее производительность. Если программа громоздка, сделайте ее быстрой и компактной.
Не оптимизируйте ее, пока не убедитесь, что это на самом деле нужно.
Несколько лет назад я работал над программой на C++, ко- торая должна была генерировать графики, помогающие ана- лизировать данные об инвестициях. Написав код расчета первого графика, мы провели тестирование, показавшее, что программа отображает график пример- но за 45 минут, что, конечно, было неприемлемо. Чтобы решить, что с этим де- лать, мы провели собрание группы. На собрании один из разработчиков выкрик- нул в сердцах: «Если мы хотим иметь хоть какой-то шанс выпустить приемлемый продукт, мы должны начать переписывать весь код на ассемблере
прямо сейчас».
Я ответил, что мне так не кажется — что около 50% времени выполнения скорее всего приходятся на 4% кода. Было бы лучше исправить эти 4% в конце работы над проектом. После еще некоторых споров наш руководитель поручил мне по- работать над производительностью программы (что мне и было нужно: «О, нет!
Только не бросай меня в тот терновый куст!»
1
).
Как часто бывает, я очень быстро нашел в коде пару ослепительных узких мест.
Внеся несколько изменений, я снизил время рисования с 45 минут до менее чем
30 секунд. Гораздо меньше 1% кода соответствовало 90% времени выполнения.
Правила оптимизации Джексо- на: Правило 1. Не делайте это- го. Правило 2 (только для экс- пертов). Не делайте этого пока
— до тех пор, пока вы не по- лучите совершенно ясное не- оптимизированное решение.
М. А. Джексон
(M. A. Jackson)
1
Часто цитируемая фраза из негритянской сказки про Братца Кролика и Братца Волка в изло- жении У. Фолкнера. —
Прим. перев.
582
ЧАСТЬ V Усовершенствование кода
Ну, а к моменту выпуска ПО нам удалось сократить время рисования почти до 1
секунды.
Оптимизация кода компилятором
Современные компиляторы могут оптимизировать код куда эффективнее, чем вам кажется. В случае, который я описал выше, мой компилятор выполнил оптимиза- цию вложенного цикла так эффективно, что я едва ли получил бы лучшие резуль- таты, переписав код. Покупая компилятор, сравните производительность каждо- го компилятора с использованием своей программы. Каждый компилятор имеет свои плюсы и минусы, и одни компиляторы лучше подходят для вашей програм- мы, чем другие.
Оптимизирующие компиляторы лучше оптимизируют простой код. Если вы жон- глируете индексами циклов и делаете другие «хитрые» вещи, компилятору будет труднее выполнить свою работу, от чего пострадает ваша программа. В подразде- ле «Размещение одного оператора на строке» раздела 31.5 вы найдете пример про- стого кода, который после оптимизации компилятором оказался на 11% быстрее,
чем аналогичный «хитрый» код.
Хороший оптимизирующий компилятор может повысить быстродействие кода на
40 и более процентов, тогда как многие из методик, описанных в следующей гла- ве, — только на 15–30%. Так почему ж просто не написать ясный код и не позво- лить компилятору выполнить свою работу? Вот результаты нескольких тестов,
показывающие, насколько успешно компиляторы оптимизировали метод встав- ки-сортировки:
Время выполне-
Время выполне- ния кода, оптими-
ния кода без
зированного
Экономия Соотношение
Язык
оптимизации
компилятором
времени
быстродействия
Компилятор C++ 1 2,21 1,05 52%
2:1
Компилятор C++ 2 2,78 1,15 59%
2,5:1
Компилятор C++ 3 2,43 1,25 49%
2:1
Компилятор C#
1,55 1,55 0%
1:1
Visual Basic
1,78 1,78 0%
1:1
Java VM 1 2,77 2,77 0%
1:1
Java VM 2 1,39 1,38
<1%
1:1
Java VM 3 2,63 2,63 0%
1:1
Единственное различие между версиями метода заключалось в том, что при пер- вой компиляции оптимизация была отключена, а при второй включена. Очевид- но, что одни компиляторы выполняют оптимизацию лучше, чем другие, а неко- торые изначально генерируют максимально эффективный код без его оптимиза- ции. Некоторые виртуальные машины Java (Java Virtual Machine, JVM) также бо- лее эффективны, чем другие. Эффективность вашего компилятора или вашей JVM
может быть другой; оцените ее сами.
1 ... 67 68 69 70 71 72 73 74 ... 104
ГЛАВА 25 Стратегии оптимизации кода
583
25.3. Где искать жир и патоку?
При оптимизации кода вы находите части программы, медленные, как патока зи- мой, и огромные, как Годзилла, и изменяете их так, чтобы они были быстры, как молния, и могли скрываться в расщелинах между байтами в оперативной памяти.
Без профилирования программы вы никогда не сможете с уверенностью сказать,
какие фрагменты медленны и огромны, но некоторые операции давно славятся ленью и ожирением, так что вы можете начать исследование именно с них.
Частые причины снижения эффективности
Операции ввода/вывода Один из самых главных источников неэффективно- сти — ненужные операции ввода/вывода. Если объем используемой памяти не иг- рает особой роли, работайте с данными в памяти, а не обращайтесь к диску, БД
или сетевому ресурсу.
Вот результаты сравнения эффективности случайного доступа к элементам 100- элементного массива «в памяти» и записям аналогичного файла, хранящегося на диске:
Время обработки
Время обработки
Экономия
Соотношение
Язык
внешнего файла
данных «в памяти»
времени
быстродействия
C++
6,04 0,000 100%
—
C#
12,8 0,010 100%
1000:1
Судя по этим результатам, доступ к данным «в памяти» выполняется в 1000 раз быстрее, чем доступ к данным, хранящимся во внешнем файле. В случае моего компилятора C++ время доступа к данным «в памяти» не удалось даже измерить.
Результаты аналогичного тестирования последовательного доступа к данным по- хожи:
Время обработки
Время обработки
Экономия
Соотношение
Язык
внешнего файла
данных «в памяти»
времени
быстродействия
C++
3,29 0,021 99%
150:1
C#
2,60 0,030 99%
85:1
Примечание: при тестировании последовательного доступа данные были в 13 раз бо- лее объемными, чем при тестировании случайного доступа, поэтому результаты двух видов тестов сравнивать нельзя.
Если для доступа к внешним данным используется более медленная среда (напри- мер, сетевое соединение), разница только увеличивается. При тестировании слу- чайного доступа к данным по сети результаты выглядят так: