Подтвердить что ты не робот

Производительность статических методов и функций

В 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

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

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

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

4b9b3361

Ответ 1

По-видимому, этот момент был исправлен в более поздних версиях 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 все еще не работает 't есть функция автозагрузки, а это означает, что Composer не может ленить загрузку вашей библиотеки, как это происходит с PSR-0/4)

Ответ 3

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

В 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);
}

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

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

Ответ 7

Я слежу за тем, что сделал Morgan Touverey Quilling, но с PHP 7. Сделано ли 3 итерации, если это займет больше времени для первого запуска, чем для последующих. Включает в себя все классы, как это может быть сделано реалистично. Все включенные файлы просто возвращают ввод.

include 'lib/global.php';
include 'SomeClass.php';
include 'StaticTest.php';

$someObj = new SomeClass();

$starttime = microtime(true);
for ($i = 0; $i< 10*100000; $i++)
    StaticTest::doStaticTest($i);

echo "<br>Static Time:   " , (microtime(true)-$starttime) , " ms\n";

// bench object method
$starttime = microtime(true);

for ($i = 0; $i< 10*100000; $i++)
    $someObj->doObjTest($i);

echo "<br>Object Time:   " , (microtime(true)-$starttime) , " ms\n";

// bench function
$starttime = microtime(true);

for ($i = 0; $i< 10*100000; $i++)
    something_doTest($i);

echo "<br>Function Time: " , (microtime(true)-$starttime) , " ms\n";

echo "<br>Static Time:   " , (microtime(true)-$starttime) , " ms\n";

// bench object method
$starttime = microtime(true);

for ($i = 0; $i< 10*100000; $i++)
    $someObj->doObjTest($i);

echo "<br>Object Time:   " , (microtime(true)-$starttime) , " ms\n";

// bench function
$starttime = microtime(true);

for ($i = 0; $i< 10*100000; $i++)
    something_doTest($i);

echo "<br>Function Time: " , (microtime(true)-$starttime) , " ms\n";

echo "<br>Static Time:   " , (microtime(true)-$starttime) , " ms\n";

// bench object method
$starttime = microtime(true);

for ($i = 0; $i< 10*100000; $i++)
    $someObj->doObjTest($i);

echo "<br>Object Time:   " , (microtime(true)-$starttime) , " ms\n";

// bench function
$starttime = microtime(true);

for ($i = 0; $i< 10*100000; $i++)
    something_doTest($i);

echo "<br>Function Time: " , (microtime(true)-$starttime) , " ms\n";

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

PHP 7.0.33

Static Time:   0.14076709747314 ms
Object Time:   0.16203689575195 ms
Function Time: 0.13194108009338 ms
Static Time:   0.13194918632507 ms
Object Time:   0.1779100894928 ms
Function Time: 0.13044309616089 ms
Static Time:   0.13045001029968 ms
Object Time:   0.16074585914612 ms
Function Time: 0.13029479980469 ms 

PHP 7.1.29

Static Time:   0.13407206535339 ms
Object Time:   0.13267111778259 ms
Function Time: 0.1302649974823 ms
Static Time:   0.13027906417847 ms
Object Time:   0.1390438079834 ms
Function Time: 0.16873598098755 ms
Static Time:   0.16874289512634 ms
Object Time:   0.13901305198669 ms
Function Time: 0.12576103210449 ms 

PHP 7.2.18:

Static Time:   0.1657600402832 ms
Object Time:   0.15700101852417 ms
Function Time: 0.1484169960022 ms
Static Time:   0.14842295646667 ms
Object Time:   0.16168689727783 ms
Function Time: 0.17508292198181 ms
Static Time:   0.17508983612061 ms
Object Time:   0.19771790504456 ms
Function Time: 0.1468551158905 ms 

PHP 7.3.5

Static Time:   0.10701704025269 ms
Object Time:   0.097011089324951 ms
Function Time: 0.075740098953247 ms
Static Time:   0.07575798034668 ms
Object Time:   0.083790063858032 ms
Function Time: 0.072473049163818 ms
Static Time:   0.072479009628296 ms
Object Time:   0.081503868103027 ms
Function Time: 0.071882963180542 ms 

PHP 7.2 работал намного медленнее, чем другие версии в среднем. Я нашел их наименьшее число, но оно тоже дошло до .2 ####. У меня нет 7,4 на данный момент.