40 votes

Pourquoi les appels de fonction PHP * sont-ils si chers?

Un appel de fonction en PHP est cher. Voici un petit benchmark pour tester:

// create test string
$string = str_repeat('a', 1000);
$maxChars = 500;

// with function call
$start = microtime(true);
for ($i = 0; $i < RUNS; ++$i) {
    strlen($string) <= $maxChars;
}
echo 'with function call: ', microtime(true) - $start, "\n";

// without function call
$start = microtime(true);
for ($i = 0; $i < RUNS; ++$i) {
    !isset($string[$maxChars]);
}
echo 'without function call: ', microtime(true) - $start;

Ce teste une fonctionnellement identique au code à l'aide d'une fonction en premier (strlen), puis sans l'aide d'une fonction (isset n'est pas une fonction).

J'obtiens le résultat suivant:

with function call:    4.5108239650726
without function call: 0.84017300605774

Comme vous pouvez le voir à la mise en œuvre à l'aide d'un appel de fonction est plus de cinq (5.38) fois plus lente que la mise en œuvre de ne pas appeler n'importe quelle fonction.

Je voudrais savoir pourquoi un appel de fonction est si cher. Quel est le principal goulot d'étranglement? Est-il la recherche dans la table de hachage? Ou de quoi est-elle si lente?


J'ai repensé à cette question, et a décidé de courir à nouveau de référence, avec XDebug complètement désactivé (pas seulement de profilage des personnes handicapées). Cela montre, que mes tests ont été assez compliquée, cette fois, avec 10000000 runs que j'ai obtenu:

with function call:    3.152988910675
without function call: 1.4107749462128

Ici un appel de fonction est seulement d'environ deux fois (2.23) comme lent, donc, la différence est de loin plus petit.


Je viens de tester le code ci-dessus sur un PHP 5.4.0 instantané et a obtenu les résultats suivants:

with function call:    2.3795559406281
without function call: 0.90840601921082

Ici, la différence a obtenu un peu plus grand encore (2.62). (Mais sur le dessus de la main, le temps d'exécution des deux méthodes a chuté de façon très significative).

47voto

Artefacto Points 50896

Les appels de fonction sont coûteux en PHP car il y a beaucoup de choses en fait.

Notez que isset n'est pas une fonction (il a un opcode), donc c'est plus rapide.

Pour un programme simple comme ceci:

<?php
func("arg1", "arg2");

Il y a six (quatre + un pour chaque argument) opcodes:

1 INIT_FCALL_BY_NAME 'func', 'func'
2 EXT_FCALL_BEGIN 
3 SEND_VAL 'arg1'
4 SEND_VAL 'arg2'
5 DO_FCALL_BY_NAME 2 
6 EXT_FCALL_END 

Vous pouvez vérifier la mise en œuvre des opcodes en zend_vm_def.h. Préfixer ZEND_ pour les noms, par exemple pour ZEND_INIT_FCALL_BY_NAME et de la recherche.

ZEND_DO_FCALL_BY_NAME est particulièrement compliqué. Puis il y a la mise en œuvre de la fonction elle-même, qui doit se détendre la pile, vérifiez les types, convertir le zvals et éventuellement de les séparer et pour le travail...

4voto

rich remer Points 835

Je maintiens qu'ils ne le sont pas. Vous n'êtes pas en train de tester un appel de fonction à tous. Vous testez la différence entre un faible niveau de vérification de limites (isset) et la marche à travers une chaîne de compter le nombre d'octets (strlen).

Je ne trouve pas d'info spécifique à PHP, mais strlen est généralement mise en œuvre de ce type (y compris l'appel de fonction, les frais généraux):

$sp += 128;
$str->address = 345;
$i = 0;
while ($str[$i] != 0) {
    $i++;
}
return $i < $length;

Une vérification de limites généralement être mis en place quelque chose comme:

return $str->length < $length;

La première est une boucle. Le second est un test simple.

3voto

Les appels de fonction sont coûteux pour la raison parfaitement expliqué par @Artefacto ci-dessus. Notez que leur rendement est directement lié au nombre de paramètres ou arguments impliqués. C'est un domaine que j'ai payé une attention particulière à tout en développant mon propre cadre d'applications. Lorsque c'est opportun et possible afin d'éviter un appel de fonction, je ne.

Un exemple récent est un remplacement de l' is_numeric() et is_integer() des appels avec un simple booléen test dans mon code, en particulier lorsque plusieurs appels à ces fonctions peuvent être faites. Alors que certains peuvent penser que de telles optimisations ont pas de sens, j'ai remarqué une nette amélioration de la réactivité de mes sites web par le biais de ce type de travail d'optimisation.

La suite de test rapide sera VRAI pour un certain nombre, et FALSE pour autre chose.

if ($x == '0'.$x) { ... }

Beaucoup plus rapide que l' is_numeric() et is_integer(). Encore une fois, seulement si elle a un sens, il est parfaitement valide pour utiliser certaines optimisations.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X