Выполнение статических методов и функций

В PHP (в отличие от того, что я изначально думал) есть накладные расходы на вызов статических методов и простых функций.

На простом сканере эти накладные расходы составляют более 30% времени вызова (метод просто возвращает параметр):

// bench static method $starttime = microtime(true); for ($i = 0; $i< 10*1000*1000; $i++) SomeClass::doTest($i); echo "Static Time: " , (microtime(true)-$starttime) , " ms\n"; // bench object method $starttime = microtime(true); for ($i = 0; $i< 10*1000*1000; $i++) $someObj->doTest($i); echo "Object Time: " , (microtime(true)-$starttime) , " ms\n"; // bench function $starttime = microtime(true); for ($i = 0; $i< 10*1000*1000; $i++) something_doTest($i); echo "Function Time: " , (microtime(true)-$starttime) , " ms\n"; 

выходы:

 Static Time: 0.640204906464 ms Object Time: 0.48961687088 ms Function Time: 0.438289880753 ms 

Я знаю, что фактическое время все еще незначительно, если только я на самом деле не называет что-то 1 миллион раз, но факт в том, что его там.

Кто-нибудь захочет попытаться объяснить, что происходит за кулисами?

Обновить:
– добавлен метод тестирования объекта

Solutions Collecting From Web of "Выполнение статических методов и функций"

По-видимому, этот момент был исправлен в более поздних версиях PHP (5.5.12).

Я запустил код OP (с пустыми методами), и я получаю следующие результаты:

 Static Time: 1.0153820514679 ms Object Time: 1.100515127182 ms 

Изменить: восемь месяцев и некоторые релизы позже …

Интересно посмотреть, как Zend и сообщество активно работают над производительностью PHP.

🐘 PHP 5.6

Вот те же тесты с PHP 5.6.9 (ZE 2.6):

 Static Time: 0.97488021850586 ms Object Time: 1.0362110137939 ms Function Time: 0.96977496147156 ms 

Для одного запуска «время объекта» было даже быстрее, чем статическое время, поэтому теперь они очень близки. Лучше, мы можем видеть, что объекты почти быстрые, как функции!

🐘 PHP 7.0

Я также скомпилировал PHP 7.0 alpha 1 (ZE 3.0), и поразительно видеть, как быстрый язык, такой как PHP (по сравнению с другими динамическими языками, как вы можете видеть здесь или здесь ) можно оптимизировать снова и снова:

 Static Time: 0.33447790145874 ms Object Time: 0.30291485786438 ms Function Time: 0.2329089641571 ms 

С PHP7 основные функции были сильно оптимизированы, а «статическое время» снова медленнее, чем «время экземпляра / объекта».

Редактировать, октябрь 2015 года через год: PHP 7.0 RC5 . Теперь «статическое время» выполняется быстрее. Важно отметить: подсказка скалярного типа (новая функция в PHP7) приносит значительные накладные расходы, она примерно на 16% медленнее (тип подсказки не делает ваш код на 16% медленнее, это медленнее, когда код только состоит из вызовов функций; ) В реальных приложениях это незначительно). Такие накладные расходы могут показаться нелогичными, но это менее неожиданно, когда вы знаете, что динамическая типизация лежит в основе PHP. В противоположность другим более статическим языкам, тип намека на PHP означает больше проверок для Zend Engine, и не меньше, чем некоторые из нас могут ожидать. В будущем мы, вероятно, получим больше оптимизаций во время выполнения (в точности как анализ кода времени выполнения HHVM и подход JiT). Не забывайте, что PHP7 молод, и вся очистка, которая была сделана для этого выпуска, позволяет в будущем улучшать функции и производительность.

🐘 HHVM

Тест против HHVM 3.7.1 все еще показывает, что HHVM легко выигрывает в таких тестах, здесь вы можете увидеть преимущества компиляции JiT (JiT является «запланированной» функцией для будущих версий PHP, мы, вероятно, ветви 7.x или 8.x. Zend создал PoC в качестве расширения OpCache ):

 Static Time: 0.070882797241211 ms Object Time: 0.23940300941467 ms Function Time: 0.06760311126709 ms 

Для HHVM функции и статические методы имеют очень схожую синхронизацию, это может позволить нам думать, что внутри они почти одинаковы (в конце концов, статический метод очень похож на функцию с именами). Экземпляр экземпляра «катастрофичен» по сравнению с другими. Это показывает, как HHVM и ZE – очень разные двигатели.

Вывод?

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

Если у вас есть выбор и / или если вы пишете библиотеку и т. Д., То, возможно, вы можете использовать методы экземпляра, более дружественные к средам DI, и это дает больше контроля разработчику, который потребляет ваш API.

Если вы просто предоставляете служебные функции (например, эти небольшие пакеты в экосистеме npm), вы можете использовать функции с именами (но имейте в виду, что PHP по-прежнему не имеет функции автозагрузки , что означает, что Composer не может ленить загрузку вашей библиотеки, например это относится к PSR-0/4)

Раньше был большой штраф при вызове статического метода, но он зафиксирован в 5.4.0 – см. Обширные результаты тестов http://www.micro-optimization.com/global-function-vs-static-method .

Я повторил тест на своей машине несколько раз и, на удивление, вы правы!

В PHP методы вызова static класса кажутся медленнее, чем вызов методов объектов. Нажмите здесь для простого теста.

Код с текущим тестом находится в приведенной выше ссылке.

Я даже попытался разместить как метод objet, так и статический метод в том же классе, и static метод все еще дает SLOWER !!!

На данный момент мне интересно, как медленным может быть вызов static метода унаследованного класса, поскольку наследование увеличивает задержку.

К сожалению, я не знаю, почему. Возможно, PHP занимает больше времени в поиске определения static метода .

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

 for($i=0; $i<10*1000*1000; $i++) { $someObj = new someObj(); $someObj->doTest($i); } 

таким образом, очевидно, медленнее, чем static вызов.

 for($i=0; $i<10*1000*1000; $i++) { SomeClass::doTest($i); } 

[*] проблема в том, сколько раз несколько раз , чтобы имитировать то, что происходит в приложении реального мира? Сложно сказать!

В ваших тестах есть что-то не так. С помощью веб-сайта, предназначенного для работы с несколькими пользователями, вы должны создать объект для каждого из них. Чтобы запустить метод этого объекта в своих тестах, вы должны:

 for($i=0; $i<10*1000*1000; $i++) { $someObj = new someObj(); $someObj->doTest($i); } 

Если у вашего объекта больше свойств и методов, тогда его создание происходит медленнее, а PHP использует больше памяти. Статический метод не будет иметь этой проблемы, и поэтому использование статических методов – лучший выбор во многих ситуациях. Например, класс с некоторыми удобными инструментами со статическими методами для общих задач.

Прошло некоторое время с тех пор, как я сделал какой-либо PHP, но это, вероятно, похоже на то, что вы ожидаете в других средах программирования.

Вполне вероятно, что статический метод требует некоторой конструкции объекта SomeClass за кулисами каждый раз, когда он вызывается, тогда как функция может быть выполнена без каких-либо затрат на запуск. Создание объекта может быть дорогостоящим в зависимости от целого ряда факторов: уничтожения существующих объектов сборщиком / счетчиком мусора, вызванным фрагментацией памяти, политикой распределения субоптимальной памяти в среде выполнения C и т. Д.

Было бы интересно сравнить эффективность метода существующего объекта. Для этого создайте экземпляр SomeClass и повторно вызовите метод экземпляра.

В случае статического метода PHP должен проверить, может ли метод быть вызван или не может быть вызван из контекста вызова (public, protected, private). Скорее всего, это вызывает накладные расходы или, по крайней мере, часть его, поскольку классический вызов функции не требует, чтобы PHP выполнял такую ​​проверку.