web-dev-qa-db-fra.com

Performance des méthodes statiques par rapport aux fonctions

En PHP, (contrairement à ce que je pensais à l'origine), il y a un surcoût pour appeler des méthodes statiques contre des fonctions simples.

Sur un banc très simple, la surcharge représente plus de 30% du temps d'appel (la méthode ne fait que renvoyer le paramètre):

// 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";

les sorties:

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

Je sais que le temps réel est toujours négligeable à moins que j'appelle réellement quelque chose 1 million de fois, mais le fait est qu'il est là.

Quelqu'un voudra-t-il essayer d'expliquer ce qui se passe dans les coulisses?

mettre à jour:
- ajout d'un banc de méthode objet

59
J.C. Inacio

Apparemment, ce point a été corrigé dans les versions ultérieures de PHP (5.5.12).

J'ai exécuté le code de l'OP (avec des méthodes vides), et j'obtiens ces résultats:

Static Time:   1.0153820514679 ms
Object Time:   1.100515127182 ms

Edit: Huit mois et quelques sorties plus tard ...

Il est intéressant de voir comment Zend et la communauté travaillent dur sur les performances de PHP.

???? PHP 5.6

Voici la même référence avec PHP 5.6.9 (ZE 2.6):

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

Pour une exécution, le "temps d'objet" était encore plus rapide que le temps statique, alors maintenant ils sont très proches. Mieux, on voit que les objets sont presque aussi rapides que les fonctions!

???? PHP 7.0

J'ai également compilé PHP 7.0 alpha 1 (ZE 3.0) et il est étonnant de voir à quel point un langage rapide comme PHP (par rapport à d'autres langages dynamiques comme vous pouvez le voir) ici ou ici ) peut être optimisé encore et encore:

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

Avec PHP7, les fonctions de base ont été grandement optimisées et le "temps statique" est encore plus lent que le "temps instance/objet".

Edit, octobre 2015 un an plus tard: PHP 7.0 RC5. Maintenant, le "temps statique" est plus rapide. Une chose importante à noter: l'indication de type scalaire (nouvelle fonctionnalité en PHP7) apporte un surcoût significatif, elle est environ 16% plus lente (l'indication de type fait pas rend votre code 16% plus lent, c'est plus lent quand vous codez n'est composé que d'appels de fonction;) Dans les applications réelles, c'est négligeable). Un tel surcoût peut sembler illogique, mais il est moins surprenant quand on sait que le typage dynamique est au cœur de PHP. Contrairement à d'autres langages plus statiques, saisissez hinting dans PHP signifie plus vérifie le moteur Zend, et non moins comme certains d'entre nous pouvaient s'y attendre. À l'avenir, nous obtiendrons probablement plus d'optimisations d'exécution sur ce point (exactement comme les analyses de code d'exécution de HHVM et l'approche JiT). N'oubliez pas que PHP7 est jeune, et tout le nettoyage qui a été fait pour cette version permet de grandes améliorations à l'avenir, en termes de fonctionnalités et de performances.

???? HHVM

Un test contre HHVM 3.7.1 montre toujours que HHVM gagne facilement sur ce type de benchmarks, ici vous pouvez voir les avantages d'une compilation JiT (JiT est une fonctionnalité "prévue" pour les futures versions de PHP, nous allons l'obtenir probablement dans les branches 7.x ou 8.x. Zend avait créé un PoC, comme une extension OpCache ):

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

Pour HHVM, les fonctions et les méthodes statiques ont un timing très similaire, cela pourrait nous faire penser que, en interne, ce sont presque les mêmes choses (après tout, une méthode statique est très similaire à une fonction à espace de noms). Le moment de l'instance est "catastrophique" par rapport aux autres. Cela montre comment HHVM et ZE sont des moteurs très différents.

Conclusion?

Il n'y a aucune garantie que l'une de ces pratiques (statique/instance) restera la plus rapide, pour toujours. Utilisez ce qui semble le meilleur en termes de conception de logiciels et conservez un code cohérent dans une application existante.

Si vous avez le choix, et/ou si vous écrivez une bibliothèque, etc., alors vous pourriez peut-être utiliser des méthodes d'instance, c'est plus convivial avec les environnements DI, et cela donne plus de contrôle au développeur qui utilise votre API.

Si vous fournissez simplement des fonctions utilitaires (comme ces petits packages dans l'écosystème de npm), vous pouvez utiliser des fonctions à espace de noms (mais sachez que PHP n'a toujours pas de fonction de chargement automatique , ce qui signifie que Composer ne peut pas charger votre bibliothèque paresseusement comme il le fait avec PSR-0/4)

53

Auparavant, il y avait une grosse pénalité lors de l'appel d'une méthode statique - mais elle est corrigée dans 5.4.0 - voir les résultats des tests détaillés http://www.micro-optimization.com/global-function-vs-static- méthode .

23
Greg

J'ai répété le test sur ma machine plusieurs fois et étonnamment vous avez raison!

Dans PHP les méthodes d'appel de la classe static semblent être plus lentes que les méthodes d'objets. Cliquez ici pour un test simple.

Le code avec le test en cours est dans le lien ci-dessus.

J'ai même essayé de placer à la fois la méthode objet et la méthode statique dans la même classe et la méthode static a toujours pour résultat SLOWER !!!

À ce stade, je me demande à quel point un appel à une méthode static d'une classe héritée peut être lent, car l'héritage ajoute du retard.

Malheureusement, je ne sais pas pourquoi. Peut-être PHP prend plus de temps à trouver la définition de la méthode static.

En guise de remarque, je ne pouvais que dire que dans une application réelle, il arrive généralement que l'objet soit créé avant d'appeler l'une de ses méthodes. Pour cela, votre test devrait prendre cela en compte en comparant la boucle d'appels statiques à une boucle qui à chaque fois (ou au moins quelques fois) [*] crée l'objet:

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

est donc évidemment plus lent que l'appel static.

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

[*] le problème est: combien coûte parfois afin de simuler ce qui se passe dans une application du monde réel? C'est difficile à dire!

7
Marco Demaio

Il y a quelque chose de mal dans vos tests. Avec un site Web conçu pour fonctionner avec plusieurs utilisateurs en même temps, vous devez créer un objet pour chacun. Pour exécuter la méthode de cet objet dans vos tests, vous devez avoir:

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

Si votre objet avait plus de propriétés et de méthodes, sa création est plus lente et PHP utilise plus de mémoire. Une méthode statique n'aura pas ce problème, et donc utiliser des méthodes statiques est un meilleur choix dans beaucoup de Par exemple, une classe avec quelques outils pratiques avec des méthodes statiques pour les tâches courantes.

6
Jquest

Cela fait un moment que je n'ai fait aucun PHP mais c'est probablement similaire à ce que vous attendez dans d'autres environnements de programmation.

Il est probable que la méthode statique nécessite une certaine construction d'un objet SomeClass en arrière-plan à chaque appel, alors que la fonction peut simplement être exécutée sans aucun coût de démarrage. La création d'un objet pourrait coûter cher en fonction d'un certain nombre de choses: destruction d'objets existants par un garbage collector/référence, pression de la mémoire provoquant la fragmentation, politiques d'allocation de mémoire sous-optimales dans le runtime C, etc.

Il serait intéressant de comparer les performances de la méthode d'un objet existant. Pour ce faire, créez une instance de SomeClass puis appelez une méthode d'instance à plusieurs reprises.

2
Brian Lyttle

Dans le cas de la méthode statique, PHP doit vérifier si la méthode peut ou ne peut pas être appelée à partir du contexte d'appel (public, protégé, privé). C'est probablement ce qui cause la surcharge, ou au moins en partie, car l'appel de fonction classique ne nécessite pas PHP pour effectuer ce type de vérification.

2
Nicolas

Je suis en train de suivre ce que Morgan Touverey Quilling a fait mais avec PHP 7. Est-ce que 3 itérations au cas où cela prend plus de temps pour la première exécution par rapport aux exécutions suivantes. Comprend toutes les classes car cela pourrait être fait de manière réaliste. Tout inclus les fichiers renvoient simplement l'entrée.

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";

Notez simplement que cela se fait sur l'un de mes hébergeurs car il est plus facile de changer de version php, il pourrait donc y avoir du bruit.

PHP 7.0.

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 semblait fonctionner beaucoup plus lentement que les autres versions en moyenne. J'ai trouvé leur nombre le plus bas, mais il est également entré dans le faible .2 ####. Je n'ai pas 7.4 en ce moment.

0
Adtopkek