ФорумПрограммированиеПыхнуть хотите?F.A.Q. → Эффективное программирование на PHP

Эффективное программирование на PHP

  • vasa_c

    Сообщения: 3127 Репутация: N Группа: в ухо

    Spritz 6 октября 2007 г. 1:59

    [size=14]Эффективное программирование на PHP[/size]

    Собираем здесь описание приемов для повышения эффективности программирования на PHP.
  • vasa_c

    Сообщения: 3127 Репутация: N Группа: в ухо

    Spritz 6 октября 2007 г. 1:59, спустя 26 секунд

    [size=12]Инструменты[/size]

    Под критериями эффективности обычно понимают следующие характеристики:
    1. Время выполнения программы.
    2. Память и другие ресурсы занимаемые программой.
    3. Время и ресурсы программиста, затрачиваемые на написание программы.

    Здесь будем в основном рассматривать первые два пункта.

    Основными инструментами по повышению эффективности программ являются — голова программиста, опыт программиста, чтение профильной литературы и товарищ Эксперимент. Однако, есть и технические средства, помогающие в этом. Наиболее важные из них — профайлеры.

    Достаточно мощным профайлером является XDebug.

    Скачать и получить информацию о нем можно на официальном сайте.

    Думаю, что юниксойды сами разберутся, как его установить. За всех пользователей Windows этого сказать не могу, поэтому вкратце объясняю:

    — Выбираем справа в разделе Windows modules ссылку PHP 5.2.1+ (а я надеюсь, у вас давно уже версия не ниже этой).
    — Скачиваем php_xdebug_2.0.0-5.2.2.dll (или, возможно, уже будет более старшая версия). Так как мы не линуксойды и работа с подобными именами файлов нам удовольствия не доставляет, переименуем файлик просто в php_xdebug2.dll.
    — Кладем полученную dll в папку с остальными библиотеками php (надеюсь, вы в курсе, где она у вас).
    — Создаем каталог для отладочной информации (например, "xdebug", внутри каталога в который установлен php).
    — Находим php.ini, дописываем в конец:

    zend_extension_ts="<абсолютный путь к папке с расширениями php>/php_xdebug2.dll"
    xdebug.profiler_enable = 1 ; Профайлер включен. Захотите выключить — поставьте 0.
    xdebug.profiler_output_dir="<абсолютный путь к только что созданной нами папке>"

    — Если php установлен, как модуль Apache, перезапускаем сервер.
    — Наслаждаемся.

    Что мы с этого получаем?
    — В случае возникновения ошибок, неперехватываемых исключений и т.п, вместо невзрачного сообщения об ошибке, мы можем лицезреть красивую табличку, в которой отображается стек функций на момент ошибки, время и место их вызова, занимаемую память и т.п.
    — var_dump теперь печатает структуру в html-формате, красиво раскрашенном :)
    Множество других фишек.
    — Ну и основное: в папку отладочной информации записывается файл с полной информацией о выполнении программы.
    Так просто прочитать этот файл несколько сложно (цифорки какие-то, значки…), поэтому качаем смотрелку WinCacheGrind, устанавливаем, запускаем, в меню открываем файл из отладочной папки, после чего можем просматривать информацию о выполняющейся программе — какие функции когда были вызваны, в каком порядке, откуда, сколько времени выполнялись, сколько памяти кушали.

    Дальше экспериментируйте и читайте документацию.

    Ссылки в догонку:
    http://phpclub.ru/faq/wakka.php?wakka=PhpProfiling
    http://www.habrahabr.ru/blog/webdev/9822.html
  • vasa_c

    Сообщения: 3127 Репутация: N Группа: в ухо

    Spritz 6 октября 2007 г. 2:00, спустя 33 секунды

    [size=12]Вступление в эффективное программирование[/size]

    Начнем вступление с ссылки на классику: "оптимизация программ на PHP".

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

    С одной стороны данная статья достаточно интересна и полезна.
    С другой она очень и очень вредна.
    Вредна не сама по себе, а по тому, как её воспринимают очень многие начинающие.

    Например, фразу $x = "test".$test, работает на 40% быстрее, чем "test $test" многие понимают, как "если заменить внедрение переменной в строку на конкатентацию строк, то программа будет работать на 40% быстрее". На самом же деле это значит только одно: "выполнение данной инструкции будет на 40% быстрее".

    Поэтому нужно четко понимать, что какие именно операции вашей программы являются "тяжелыми". Какие стоит оптимизировать, а с какими не стоит возиться.
    Например, у вас программа выполняется ровно 1(одну) секунду.
    Она содержит в себе несколько SQL-запросов, запись в файл и отправку данных по сети. Эти операции в сумме выполняются 0,95 секунды.
    Таким образом, получается, что всё остальное выполняется 0,05 сек (обычно так и есть — внешний обмен данными занимает куда больше времени, чем внутренняя обработка).
    Вы можете отчаянно вылизывать конструкции языка, заменять двойные кавычки на одинарные, минимизировать количество переменных, слить все используемые файлы в один, удалить комментарии, записать всю программу в одну длинную строку. Своими действиями вы можете значительно повысить скорость выполнения оптимизированных конструкций, например, на действительно впечатляющие 40%. И теперь вместо 50 миллисекунд, они выполняются 30. Вся же программа будет выполняться 0,98 секунды. То есть вместо сорока, вы выиграли два процента. Стоило оно того? Каждый решает сам.

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

    Многие проводят оптимизацию на основании умственных заключений на основании того, как по их мнению работает интерпретатор. А очень часто он работает совершенно по другому. Многие переносят приемы оптимизации из языков с которыми они работали раньше. И опять таки эти приемы часто не работают с PHP или даже работают в обратную сторону. Ниже мы постараемся рассмотреть некоторые из этих заблуждений, однако, ни рассмотреть, ни даже просто знать их все, невозможно. Тем более, что язык постоянно развивается. Поэтому каждый раз, когда вам кажется, что один прием будет работать быстрее другого, подкрепляйте свои рассуждения экспериментом.
  • vasa_c

    Сообщения: 3127 Репутация: N Группа: в ухо

    Spritz 6 октября 2007 г. 2:01, спустя 49 секунд

    [size=12]PHP — транслятор[/size]

    PHP называют интерпретатором, хотя он перестал им быть с древних времен PHP3. PHP — транслятор.
    Интерпретатор интерпретирует каждую строчку последовательно. Если эта строчка — часть тела цикла на тысячу итераций, то она будет интерпретирована тысячу раз.
    Транслятор первым делом разбирает код и переводит (транслирует) его во внутреннее представление (байт-код). Дальше работа происходит с этим байт кодом. Поэтому не надо заниматься "оптимизацией" кода циклов или тел часто используемых функций (удалением комментариев, пробелов и т.п), оптимизация будет произведена на этапе разбора исходного текста в байт код.


    Подключение файлов

    PHP установленный, как модуль сервера или как FastCGI, кеширует подключаемые файлы. Мало того, он кеширует не сами файлы, а их байт-код. Поэтому не следует избегать лишних require(), не нужно сливать как можно больше кода в один файл. При нормальном кешировании это не принесет больших дивидендов.
    Хотя и тут есть ньюансы — вариант установки PHP, размер кеша и др.


    Optimizer

    Как мы выяснили выше, выполнение сценария разбивается на:
    1. Трансляцию исходного кода в байт-код.
    2. Выполнение байт-кода.

    Есть продукты, позволяющие избежать 1-го этапа. Т.е. перевести исходники в байт-код заранее и хранить в файлах именно его. Например, это парочка ZendEncoder/ZendOptimizer.
    Очень часто данная вещь используется для кодирования программ, с целью защиты исходников, однако, помните, что ZendOptimizer изначально все-таки Optimizer, т.е. использование его позволяет значительно оптимизировать время выполнения сценария.
  • vasa_c

    Сообщения: 3127 Репутация: N Группа: в ухо

    Spritz 6 октября 2007 г. 2:49, спустя 48 минут 1 секунду

    [size=12]Копирование данных[/size]

    Оптимизация копирования данных некоторыми программистами, является классическим примером оптимизации на основании умственных заключений, без проверки этих заключений экспериментом.

    Как говорится в документации: "при присвоении массивов $B = $A, происходит копирование их элементов", "при передаче массива по значению, происходит копирование данных". Ну и первая мысль начинающего оптимизатора — а я всех обману, я буду передавать по ссылке. И он начинает передавать по ссылке, отслеживать моменты когда по логике программы в двух переменных должны быть одинаковые значения, а когда одна из них изменяется. На самом деле не нужно этого делать, потому что всё это сделает сам PHP. Так как в нем реализована такая забавная фишка, как отложенное копирование.

    Пример:

    $A = Array(/* Огромный массив */);
    $B = $A;

    Теоретически здесь происходит копирование и получается два огромных массива. Практически никаких пересылок в памяти не происходит и новая память не занимается. По логике программы, $A и $B здесь содержат одинаковые значения. И доступ к ним будет одинаков:

    print $A[5];
    print $B[5]; // Одно и то же
    print $A[5] == $B[5];

    Поэтому, вместо копирования на данном этапе происходит связывание переменной $B с массивом в памяти, на который указывает $A. Т.е. $A и $B разделяют одну область памяти. Если в дальнейшем до выхода этих переменных из области видимости с ними ничего не приключится, то копирования не произойдет и в последствии.
    Приключиться же может следующее:

    $A[5] = 10; // Изменение значения элемента одного из массивов.

    Здесь по логике программы, содержимое массивов уже будет различно, поэтому именно в этот момент происходит копирование данных, $B привязывается к новому массиву, а в изначальном массиве изменяется один элемент.

    Заметьте, что это происходит только при изменении структуры данных, а не при присвоении нового значения одной из переменных:

    $A = 10;

    Здесь просто $A рвет ссылку с массивом и начинает указывать на 10. $B же остается единственной переменной, указывающей на изначальный массив. Никакого отложенного копирования не происходит и уже не произойдет, даже при изменении структуры $B.

    Еще пример (обмен значениями):

    $A = Array(/* Огромный массив */);
    $B = Array(/* Еще один */);
    $tmp = $A;
    $A = $B;
    $B = $tmp;

    Две переменные обменялись двумя массивами, при этом никакого копирования не произошло. Только не забываем, что теперь $B и $tmp связаны и при изменении массива $B произойдет копирование. Поэтому от греха подальше лучше сделать unSet($tmp).

    Всё это действует и при передаче переменных по значению в функции, при возвращении значений из функций и так далее.

    Правда, иногда можно воспользоваться и передачей по ссылке:

    function func($A)
    {
    $A[0] = $A[0] + $A[1];
    }
    $A = Array(/* … */);
    func($A);


    При передаче массива в функцию копирования не произойдет, но оно произойдет при изменении локального массива внутри функции. Поэтому, если массив не будет использоваться нигде дальше в программе, можно передать его по ссылке func(&$A), хотя в последних версиях возможность передачи в функцию аргументов как по ссылке, так и по значению не приветствуется.


    Так что кроме высказывания множества "фи" разработчикам PHP, можно заодно сказать им большое спасибо за данную технологию. На многих задачах время исполнения и занимаемая память существенно снижается.

    PS. Ну и нужно понимать, что фразы "$A и $B ссылаются на один и тот же массив" к "ссылкам" в PHP не имеют никакого отношения. С точки зрения прикладного программиста там происходит именно копирование данных и программа ведет себя так же, как и при копировании, за исключением скорости выполнения.
    Нужно только не пытаться оптимизировать там, где уже всё оптимизировано. Ну и так же помнить об этих связях и пытаться избегать срабатывания настоящего копирования в памяти.
  • kendo

    Сообщения: 446 Репутация: N Группа: Адекваты

    Spritz 6 декабря 2007 г. 3:04, спустя 61 день 1 час 15 минут

    Цитата из книги \"Профессиональное программирование на PHP\"

    К сожалению, PHP не всегда в состоянии оптимизировать исполнение циклов. Например, в следующем примере вызов функции
    count($array)
    будет производиться в начале каждой итерации.

    for ($i=0; $i<count($array); $i++) {
    }

    Поэтому, чтобы сократить накладные расходына ненужные вычисления длины массива, этот фрагмент следует записать в следующем виде:

    $count=count($array);
    for ($i=0; $i<$count; $i++) {
    }


    Еще один элегантный способ избежать подобных накладных расходов основан на множественном вычислении начальных выражения оператора for:

    for ($count=count($array), $i=0; $i<$count; $i++) {
    }

  • adw0rd

    Сообщения: 22902 Репутация: N Группа: в ухо

    Spritz 6 декабря 2007 г. 3:40, спустя 35 минут 8 секунд

    KENDO спасибо за заметку :)
    adw/0
  • Patrick

    Сообщения: ? Репутация: N Группа: Кто попало

    Spritz 6 декабря 2007 г. 4:54, спустя 1 час 14 минут 7 секунд

    PHP установленный, как модуль сервера или как FastCGI, кеширует подключаемые файлы.


    C чего ты это взял? Для кэширования optcode есть extension, которые никак не входят в ядро(хотя насколько мне извесно APC будет в php6)


    про ZendOptimizer первый раз от тебя слышу…
  • vasa_c

    Сообщения: 3127 Репутация: N Группа: в ухо

    Spritz 6 декабря 2007 г. 5:16, спустя 22 минуты 12 секунд

    Что не так с оптимайзером?
  • Patrick

    Сообщения: ? Репутация: N Группа: Кто попало

    Spritz 6 декабря 2007 г. 5:50, спустя 33 минуты 51 секунду


    Что не так с оптимайзером?

    Я не где просто не слушал что он увеличивает производительность….
  • AlexB

    Сообщения: 4288 Репутация: N Группа: в ухо

    Spritz 6 декабря 2007 г. 6:10, спустя 19 минут 49 секунд


    Я не где просто не слушал что он увеличивает производительность….
    Увеличивает, причем достаточно существенно.
  • sap

    Сообщения: ? Репутация: N Группа: Кто попало

    Spritz 10 июля 2008 г. 5:45, спустя 216 дней 22 часа 35 минут

    Добавлю немного от себя. Некоторые моменты пересекаются, но есть и новое.
    http://s-a-p.in/interactive/theme/basis-optimization

Пожалуйста, авторизуйтесь, чтобы написать комментарий!