Pull to refresh

Строки в PHP

Reading time4 min
Views3.1K
В последнее время обсуждения языка PHP на хабре сводятся больше к возможностям проектировать сложные системы, что не может не радовать. Однако, просмотрев с десяток самых признаваемых веб-фреймворков (Zend Framework, Adept, CakePHP, CodeIgniter, LIMB, Symfony, MZZ и другие) я с искренним удивлением обнаружил в некоторых существенные недочеты с точки зрения элементарной оптимизации.

Одним из слабых мест является работа со строками (проблема конкатенации уже обсуждалась хабрапрограммистами) и мне решилось вспомнить свою познавательную юность и провести парочку тестов со строками, которыми мне хотелось бы поделиться.



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

Итак, поехали… Задача предельно проста: провести эксперименты по скорости формирования строк из подстрок в одинарных и двойных кавычках. В принципе, этот вопрос будет актуален еще долгое время в связи с особенностями обработки строк в PHP.

Существует немало статей по базовой оптимизации скриптов как на русском, так и на других языках. Про строки там говорится мало, но отмечается факт «парсинга» строк в двойных кавычках на предмет переменных и управляющих символов (врочем, как и в официальной документации). На основе этого логично предположить, что использование строк в двойных кавычках в работе будет несколько медленнее, чем те же операции с подстроками в одинарных кавычках.

Кроме подстановки переменных в строки и конкатенации переменных с подстроками, в PHP реализован еще как минимум один способ формирования строк: работа с функцией sprintf. Логично предположить, что данный метод будет существенно уступать «стандартным» из-за лишнего вызова функции и парсинга строки внутри.

Единственное дополнение, перед тем, как я представлю вам код тестового скрипта: необходимо учитывать 2 возможных варианта работы со строками в двойных кавычках: с учетом простого и «продвинутого» стиля кодирования. На то, что переменные стоят в самом начале строк обращать внимания не стоит, наверное — они являются только примерами:
 $string = "$_SERVER['HTTP_HOST'] — не администрация Ульяновской области. Мы любим русский язык и не любим тех, кто его ..."

и
 $string = "{$_SERVER['HTTP_HOST']} — не администрация Ульяновской области. Мы любим русский язык и не любим тех, кто его ..."


Тест номер один.
Ну, вроде бы, все оговорки сделаны — пора показывать результаты работы. Исходный код тестировщика можно найти здесь.

Скриншоты профайлера (копии) располагаются тут, тут и тут.

По скриншотам видно, что моя гипотеза не подтвердилась. Единственно-верным оказалось предположение о работе со строками через sprintf. Наиболее быстрыми оказались функции, работающие именно с двойными кавычками.

После непродолжительного обдумывания ситуации объяснение пришло само собой: все дело в том, что эталонная строка, в которую производились подстановки является слишком короткой: проход парсера по такой строке — дело плевое. Однако, даже тут видно, что нативная подстановка переменной в строку дает преимущество перед «продвинутым стилем».
В этом же и слабость подхода конкатенации: объемы вставляемых данных превышают объемы подстрок. Откуда берутся накладные расходы можно прочесть в уже упомянутом хабратопике.

Однако, даже эти мысли нужно было подтвердить. Для этого понадобился второй тест с изменениями на возможные упомянутые причины столь непредсказуемого (для меня) поведения. Видимо уж очень многое подкрутили в пятой версии (признаюсь, в пятой версии php я проводил только 1 тест: на обход элементов массивов).

Тест номер два.
Вторая гипотеза: удлинение эталонной строки даст, в конечном счете, увеличение процентного отношения времени работы функций-тестировщиков, связанных с формированием строк в двойных кавычках, относительно результатов теста номер 1. Такая же ситуация, теоретически, должна наблюдаться и в отношении работы функции sprintf. Связано это, в первую очередь, с необходимостью парсинга строк и увеличением времени, затраченных на это. В ситуации с конкатенацией подстрок в одинарных кавычках, думаю, будет наблюдаться примерно тот же результат, что и в первом тесте, что даст небольшое уменьшение доли времени выполнения функции quotes_3() ко времени работы всего скрипта (но никак не прирост производительности).

Исходники скрипта лежат здесь,
Копии скриншота можно найти тут, тут и тут.

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

Предположение относительно строк в одинарных кавычках так же оказалось верным: вместо 36,75% времени в первом тесте, во втором функция quotes_3() заняла 33,76% времени исполнения скрипта

Практическая ценность.
Говоря простым языком, абстрагируясь от данных, можно сделать вывод: чем длиннее строка, в которой необходимо произвести подстановку, тем больше вероятность, что операция-конкатенация выполнится быстрее, чем поиск переменной в двойных кавычках. Добровольцы могут попробовать подобрать необходимые параметры вставки (количество переменных, длина эталонной строки, длины строк в переменных) такими, чтобы они удовлетворяли равенству времен выполнения.

Вот, собственно, и все. Остается только добавить, что в программировании мелочей не бывает (это я любителям сказать «экономия на спичках» (с) Adelf ). Зная такие тонкости и принимая их во внимание, можно писать код, который будет оптимизирован на всех своих уровнях ;)

PS:
Тесты проведены с помощью Zend Studio For Eclipse 6.0.0 (Debugger + Profiler included).
Версия PHP 5.2.5
ОС Debian Linux

PPS:
Буду рад, если кто-то выложит свои результаты данных тестов. Думаю, это позволит более объективно составить оценку необходимости использования того либо иного метода подстановки в строки. Так же буду признателен здоровой критике стиля изложения и оформления.

Всем спасибо за внимание :)
Tags:
Hubs:
-2
Comments29

Articles

Change theme settings