Question

En PHP, les paramètres de fonction peuvent être passés par référence en ajoutant un paramètre esperluette au paramètre dans la déclaration de fonction, comme suit:

function foo(&$bar)
{
    // ...
}

Je suis maintenant conscient du fait que cette non n'est pas conçue pour améliorer les performances, mais pour permettre aux fonctions de modifier les variables qui sont normalement hors de leur champ d'application.

Au lieu de cela, PHP semble utiliser Copy On Write pour éviter de copier des objets (et peut-être aussi des tableaux) jusqu'à ce qu'ils soient modifiés. Ainsi, pour les fonctions qui ne changent pas leurs paramètres, l’effet devrait être le même que si vous les aviez passées par référence.

Cependant, je me demandais si la logique de copie à l'écriture était court-circuitée lors de la référence par référence et si cela avait un impact sur les performances.

ETA: Bien sûr, je suppose que ce n’est pas plus rapide, et je suis bien consciente que ce n’est pas à quoi servent les références. Je pense donc que mes propres hypothèses sont plutôt bonnes. Je cherche simplement une réponse de la part de quelqu'un qui sait vraiment ce qui se passe sous le capot. En cinq ans de développement de PHP, j'ai toujours eu du mal à obtenir des informations de qualité sur les composants internes de PHP dès la lecture du code source.

Était-ce utile?

La solution

Le moteur Zend Engine utilise la copie sur écriture et lorsque vous utilisez une référence vous-même, cela entraîne une légère surcharge. Ne peut trouver que cette mention au moment de la rédaction de cet article cependant, et les commentaires dans le manuel contiennent d'autres liens.

(EDIT) La page de manuel sur Objets et références . contient un peu plus d'informations sur la façon dont les variables d'objet diffèrent des références.

Autres conseils

Lors d’un test avec 100 000 itérations d’appel d’une fonction avec une chaîne de 20 ko, les résultats sont les suivants:

Fonction qui lit / utilise simplement le paramètre

pass by value:      0.12065005 seconds
pass by reference:  1.52171397 seconds

Fonction pour écrire / changer le paramètre

pass by value:      1.52223396 seconds
pass by reference:  1.52388787 seconds

Conclusions

  1. Passer le paramètre par valeur est toujours plus rapide

  2. Si la fonction change la valeur de la variable transmise, est pratiquement identique à transmettre par référence à par valeur

J'ai fait quelques tests à ce sujet parce que je n'étais pas sûr des réponses données.

Mes résultats montrent que le passage de grands tableaux ou chaînes par référence EST nettement plus rapide.

Voici mes résultats: Indice de référence

L'axe des ordonnées (exécutions) représente le nombre de fois qu'une fonction peut être appelée en 1 seconde * 10

.

Le test a été répété 8 fois pour chaque fonction / variable

Et voici les variables que j'ai utilisées:

$large_array = array_fill(PHP_INT_MAX / 2, 1000, 'a');
$small_array = array('this', 'is', 'a', 'small', 'array');
$large_object = (object)$large_array;
$large_string = str_repeat('a', 100000);
$small_string = 'this is a small string';
$value = PHP_INT_MAX / 2;

Ce sont les fonctions:

function pass_by_ref(&$var) {
}

function pass_by_val($var) {
}

J'ai expérimenté des valeurs et des références d'une chaîne de 10 ko en les passant à deux fonctions identiques. On prend argument par valeur et le second par référence. C'étaient des fonctions communes: prendre des arguments, effectuer un traitement simple et renvoyer une valeur. J'ai passé 100 000 appels des deux et j'ai compris que les références n'étaient pas conçues pour améliorer les performances - le bénéfice de référence était d'environ 4-5% et il ne grossit que lorsque la chaîne devient suffisamment grosse (100k et plus, ce qui donne une amélioration de 6-7%) . Donc, ma conclusion est que n'utilisez pas de références pour améliorer les performances, ce genre de choses n'est pas pour cela.

J'ai utilisé PHP version 5.3.1

Je suis à peu près sûr que non, ce n'est pas plus rapide. De plus, il est expressément indiqué dans le manuel de ne pas utiliser de références pour améliorer les performances.

Modifier: Vous ne trouvez pas où il est écrit cela, mais c'est là!

Il n'y a rien de mieux qu'un morceau de code test

<?PHP
$r = array();

for($i=0; $i<500;$i++){
$r[]=5;
}

function a($r){
$r[0]=1;
}
function b(&$r){
$r[0]=1;
}

$start = microtime(true);
for($i=0;$i<9999;$i++){
  //a($r);
  b($r);
}
$end = microtime(true);

echo $end-$start;
?>

Résultat final! Plus le tableau est grand (ou plus le nombre d'appels est élevé), plus la différence est grande. Ainsi, dans ce cas, l'appel par référence est plus rapide car la valeur est modifiée dans la fonction.

Dans le cas contraire, il n'y a pas de réelle différence entre "par référence". et "par valeur", le compilateur est suffisamment intelligent pour ne pas créer une nouvelle copie à chaque fois s’il n’est pas nécessaire.

J'ai essayé de comparer cela à un exemple concret basé sur un projet sur lequel je travaillais. Comme toujours, les différences sont triviales, mais les résultats étaient quelque peu inattendus. Pour la plupart des tests que j'ai vus, la fonction appelée ne modifie pas la valeur transmise. J'ai effectué une simple str_replace () dessus.

**Pass by Value Test Code:**

$originalString=''; // 1000 pseudo-random digits

function replace($string) {
    return str_replace('1', 'x',$string);
}
$output = '';
/* set start time */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tstart = $mtime;
set_time_limit(0);

for ($i = 0; $i < 10; $i++ ) {
    for ($j = 0; $j < 1000000; $j++) {
        $string = $originalString;
        $string = replace($string);
    }
}

/* report how long it took */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tend = $mtime;
$totalTime = ($tend - $tstart);
$totalTime = sprintf("%2.4f s", $totalTime);
$output .= "\n" . 'Total Time' .
    ': ' . $totalTime;
$output .= "\n" . $string;
echo $output;

Code de test de passage par référence

Idem sauf pour

function replace(&$string) {
    $string = str_replace('1', 'x',$string);
}
/* ... */
replace($string);

Résultats en secondes (10 millions d'itérations):

PHP 5
    Value:     14.1007
    Reference: 11.5564

PHP 7
    Value:     3.0799
    Reference: 2.9489

La différence est d'une fraction de milliseconde par appel de fonction, mais pour ce cas d'utilisation, le passage par référence est plus rapide en PHP 5 et PHP 7.

(Remarque: les tests de PHP 7 ont été effectués sur une machine plus rapide - PHP 7 est plus rapide, mais probablement pas beaucoup plus rapide.)

C'est simple, il n'y a pas besoin de tester quoi que ce soit. Dépend du cas d'utilisation.

Le passage par valeur sera TOUJOURS PLUS RAPIDE PAR VALEUR que la référence pour une petite quantité d’arguments. Cela dépend du nombre de variables que l'architecture permet de transmettre au travers de registres (ABI).

Par exemple, x64 vous permettra de transmettre 4 valeurs de 64 bits à travers des registres. https://en.wikipedia.org/wiki/X86_calling_conventions

En effet, vous n'avez pas besoin de dé-référencer les pointeurs, il vous suffit d'utiliser la valeur directement.

Si vos données à transmettre sont supérieures à ABI, les autres valeurs seront empilées. Dans ce cas, un tableau ou un objet (qui est par exemple une classe ou une structure + en-têtes) sera TOUJOURS PLUS RAPIDE PAR RÉFÉRENCE.

En effet, une référence est simplement un pointeur sur vos données (pas les données elles-mêmes), taille fixe, disons 32 ou 64 bits selon la machine. Ce pointeur tiendra dans un registre de la CPU.

PHP étant écrit en C / C ++, je m'attendrais à ce qu'il se comporte de la même manière.

Il n'est pas nécessaire d'ajouter & amp; opérateur lors du passage d'objets. En PHP 5+, les objets sont quand même passés par référence.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top