11 votes

Est-ce que l'annulation des valeurs du tableau lors de l'itération permet d'économiser de la mémoire?

C'est une question de programmation simple, venant de mon manque de connaissance sur la façon dont PHP gère la copie et la suppression d'éléments de tableau pendant une boucle foreach. C'est comme ceci, j'ai un tableau qui me vient d'une source externe formaté de la façon dont je veux le changer. Un exemple simple serait :

$myData = array('Clé1' => array('valeur1', 'valeur2'));

Mais ce que je veux serait quelque chose comme :

$myData = array([0] => array('MaClé' => array('Clé1' => array('valeur1', 'valeur2'))));

Je prends donc le premier $myData et le formate comme le deuxième $myData. Je suis totalement à l'aise avec mon algorithme de mise en forme. Ma question porte sur la recherche d'un moyen de conserver la mémoire puisque ces tableaux pourraient devenir un peu encombrants. Ainsi, pendant ma boucle foreach je copie la ou les valeurs actuelles du tableau dans le nouveau format, puis je supprime la valeur avec laquelle je travaille du tableau d'origine. Par exemple :

$formattedData = array();
foreach ($myData as $key => $val) {
    // faire un peu de mise en forme ici, copier dans $reformattedVal

    $formattedData[] = $reformattedVal;

    unset($myData[$key]);
}

Est-ce que l'appel à unset() est une bonne idée ici ? Autrement dit, est-ce que cela sauvegarde de la mémoire puisque j'ai copié les données et n'ai plus besoin de la valeur originale ? Ou est-ce que PHP collecte automatiquement les déchets car je ne les référence pas dans un code ultérieur ?

Le code fonctionne bien, et jusqu'à présent mes ensembles de données ont été trop négligeables en taille pour tester les différences de performances. Je ne sais juste pas si je me prépare à des bogues bizarres ou à des ralentissements CPU plus tard.

Merci pour toute idée.
-sR

5voto

Amos Wright Points 16

Je manquais de mémoire en traitant des lignes d'un fichier texte (xml) dans une boucle. Pour toute personne dans une situation similaire, cela a fonctionné pour moi:

while($data = array_pop($xml_data)){
     //traiter $data
}

4voto

Andy Points 8860

Utilisez une référence à la variable dans la boucle foreach en utilisant l'opérateur &. Cela évite de créer une copie du tableau en mémoire pour que foreach puisse itérer.

éditer: comme souligné par Artefacto détruire la variable ne fait que diminuer le nombre de références à la variable d'origine, donc la mémoire sauvegardée ne concerne que les pointeurs et non la valeur de la variable. Bizarrement, l'utilisation d'une référence augmente en fait l'utilisation totale de la mémoire car apparemment la valeur est copiée à un nouvel emplacement mémoire au lieu d'être référencée.

À moins que le tableau soit référencé, foreach fonctionne sur une copie du tableau spécifié et non sur le tableau lui-même. foreach a quelques effets secondaires sur le pointeur du tableau. Ne vous fiez pas sur le pointeur du tableau pendant ou après le foreach sans le réinitialiser.

Utilisez memory_get_usage() pour identifier combien de mémoire vous utilisez.

Il y a un bon article sur l'utilisation et l'allocation de mémoire ici.

Ceci est un code de test utile pour voir l'allocation de mémoire - essayez de décommenter les lignes commentées pour voir l'utilisation totale de mémoire dans différents scénarios.

echo memory_get_usage() . PHP_EOL;
$test = $testCopy = array();
$i = 0;
while ($i++ < 100000) {
    $test[] = $i;
}
echo memory_get_usage() . PHP_EOL;
foreach ($test as $k => $v) {
//foreach ($test as $k => &$v) {
    $testCopy[$k] = $v;
    //unset($test[$k]);
}
echo memory_get_usage() . PHP_EOL;

3voto

Andy Lester Points 34051

N'oubliez pas les règles du Club d'Optimisation :

  1. La première règle du Club d'Optimisation est : vous ne devez pas optimiser.
  2. La deuxième règle du Club d'Optimisation est : vous ne devez pas optimiser sans mesurer.
  3. Si votre application fonctionne plus rapidement que le protocole de transport sous-jacent, l'optimisation est terminée.
  4. Un facteur à la fois.
  5. Pas de marketroids, pas de planning de marketroid.
  6. Les tests se poursuivront aussi longtemps qu'il le faudra.
  7. S'il s'agit de votre première nuit au Club d'Optimisation, vous devez rédiger un cas de test.

Les règles n°1 et n°2 sont particulièrement pertinentes ici. Sauf si vous savez que vous devez optimiser, et sauf si vous avez mesuré ce besoin d'optimisation, alors ne le faites pas. Ajouter l'inutile entraînera une surcharge de temps d'exécution et fera se demander aux futurs programmeurs pourquoi vous le faites.

Laissez-le tel quel.

2voto

Artefacto Points 50896

Si à n'importe quel moment dans le "formatage" vous faites quelque chose comme :

$reformattedVal['a']['b'] = $myData[$key];

Alors faire unset($myData[$key]); est sans importance en termes de mémoire car vous ne faites que diminuer le compte de référence de la variable, qui existe maintenant à deux endroits (à l'intérieur de $myData[$key] et $reformattedVal['a']['b']). En réalité, vous économisez la mémoire de l'indexation de la variable à l'intérieur du tableau d'origine, mais c'est presque négligeable.

0voto

middaparka Points 33832

Sauf si vous accédez à l'élément par référence, le unset ne fera absolument rien, car vous ne pouvez pas modifier le tableau pendant l'itération.

Cela dit, il est généralement considéré comme une mauvaise pratique de modifier la collection sur laquelle vous itérez - une meilleure approche serait de diviser le tableau source en petits morceaux (en ne chargeant qu'une partie des données source à la fois) et de traiter ceux-ci, en supprimant chaque "morceau" de tableau entier au fur et à mesure.

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