28.3. Оптимизация самых медленных частей программного кода
Несмотря на то что присутствует и другая мотивация, которой, например, можно считать личное удовлетворение, программы чаще всего оптимизируются для того, чтобы сэкономить денежные средства. И это нельзя упускать из виду, проводя время за улучшением рабочих характеристик ваших программ. Нет никакого смысла тратить время на оптимизацию, которая не несет никаких выгод. Оптимизация приложений в большинстве случаев окупает потраченное на него время, особенно при получении дохода с лицензии. Нельзя оценить в денежном выражении оптимизацию бесплатного приложения, но не следует забывать, что работа над открытыми проектами может стать отличным отдыхом.
Для того чтобы сэкономить время и усилия, попробуйте оптимизировать самые медленные части вашей программы, где можно получить максимальный выигрыш. Как правило, начинать необходимо на самой ранней стадии работы над проектом, с оптимизации алгоритмов, пытаясь найти более быстрые альтернативы. Эффективность алгоритмов описывается с помощью специальной формы записи, которая называется нотацией "большого O". Так, алгоритм, проверяющий каждое вводимое значение один раз, можно представить как O(n). Алгоритм, проверяющий вводимые значения дважды, также будет обозначен как O(n), так как линейные коэффициенты не увеличивают сложность. По-настоящему медленный алгоритм будет иметь обозначение O(nA2), или O от n в квадрате. Действительно быстрый алгоритм имеет обозначение O(n log n), или n, умноженное на логарифм от n. Эта тема достаточно сложная, чтобы углубиться в нее здесь, и детальное описание ее можно найти в Internet или в университетском курсе. Понимание этой проблематики помогает создавать быстрые алгоритмы.
Постоянные запоминающие устройства типа жестких дисков работают медленнее оперативной памяти (ОЗУ). Операционные системы компенсируют этот недостаток, кэшируя диски в оперативной памяти, но всю систему кэшировать невозможно. Те части программы, в которых используется постоянная запись, являются первыми претендентами на оптимизацию.
Для данных, хранящихся в файлах, лучше использовать реляционную базу данных. Серверы баз данных лучше справляются с кэшированием данных, чем операционная система, так как они оперируют данными с применением большей детализации. Серверы баз данных также могут кэшировать открытые файлы, позволяя избегать накладных расходов, связанных с открытием и закрытием файлов.
С другой стороны, можно собственноручно запрограммировать кэширование данных, но при этом необходимо принимать в расчет время выполнения сценария PHP. В конце запроса PHP освобождает всю память. Если во время выполнения программы необходимо обратиться к одному и тому же файлу несколько раз, можно получить существенный выигрыш от считывания всего файла в одну переменную.
Кроме того, можно попробовать оптимизировать запросы к базе данных. Так, MySQL включает оператор EXPLAIN, возвращающий информацию о том, как механизм объединения использует индексы. Руководство по MySQL содержит информацию о процедуре оптимизации запросов.
Можно также обратить внимание на циклы. Если число итераций цикла невелико, можно существенно выиграть от замены цикла последовательностью повторяющихся операторов. Рассмотрим, например, цикл for, в котором элементам массива присваивается 10 значений. Этот цикл спокойно можно заменить 10 операторами, которые представляют собой повторение кода и могут выполняться значительно быстрее.
Постарайтесь не вычислять значения внутри цикла. Если в цикле используется размерность массива, вместо постоянного вызова значения count воспользуйтесь пе­ременной для сохранения размерности до входа в цикл. Просмотрите части математических выражений и замените, если это возможно, переменные константами.
Вызовы функций связаны с достаточно большими издержками. Ограничив использование функций, можно значительно повысить быстродействие сценария. В компилируемых языках типа C и Java пользуются механизмом замены вызываемых функций кодом. По возможности необходимо избегать функций, которые вызываются один раз. Техника создания читаемого кода позволяет скрыть детали, однако использование такой техники в PHP приводит к большим затратам.
Если все возможности оптимизации исчерпаны, можно прибегнуть еще к одному приему: перевести PHP-код в код C, представив его в виде PHP-функции. Хотя этот прием и не рассчитан на новичков, но многие функции PHP начинались после попытки оптимизации. Рассмотрим пример функции in_array. Можно проверить значение в массиве, просмотрев массив в цикле, но использование функции, написанной на C, даст существенный выигрыш в производительности.