IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Demande d'explications sur comportement avec stockage de références en tableau


Sujet :

Langage PHP

  1. #1
    Membre émérite Avatar de Djakisback
    Profil pro
    Inscrit en
    Février 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 2 022
    Points : 2 273
    Points
    2 273
    Par défaut Demande d'explications sur comportement avec stockage de références en tableau
    Bonjour,
    j'essaie de gagner de la RAM sur un script et il y a quelque chose que je ne comprends pas avec de la copie de références. Je veux stocker des références vers des string dans un tableau plutôt que de stocker la valeur réelle pour réduire la consommation mémoire.

    Ce que je ne comprends pas c'est que si je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    $str = "str1";
    $array1[] = $str;
    $array1[] =& $str;
     
    echo '<pre>';
    var_dump($array1);
    echo '</pre>';
    J'ai bien comme sortie :

    array(2) {
    [0]=>
    string(4) "str1"
    [1]=>
    &string(4) "str1"
    }
    On peut voir que array1[1] pointe bien vers &$str.

    En revanche si je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $array2[] = "str2";
    $array2[] =& $array2[0];
     
    echo '<pre>';
    var_dump($array2);
    echo '</pre>';
    (et c'est ça qui m'intéresse plutôt que le cas 1), j'obtiens :

    array(2) {
    [0]=>
    &string(4) "str2"
    [1]=>
    &string(4) "str2"
    }
    En fait, je m'attendais plutôt à avoir ceci :

    array(2) {
    [0]=>
    string(4) "str2"
    [1]=>
    &string(4) "str2"
    }
    et j'ai du mal à comprendre ce comportement. Les 2 entrées du tableau semblent pointer vers une référence sur un string anonyme.

    En tout cas cela semble fonctionner, toutes les entrées du tableau pointent bien vers la même zone mémoire mais le var_dump() me trouble un peu

    D'ailleurs, peut-être que cette manip est déjà faite de manière transparente par PHP (à la manière des string immuables en Java) ? Si quelqu'un a des infos sur tout ça
    Merci d'avance.

    (Par ailleurs, ça fait un moment que je n'avais pas fait de php, si quelqu'un a des infos sur l'obsolescence éventuelle des références, comme cela avait été annoncée il y a plusieurs années... merci ^_^)
    Vive les roues en pierre

  2. #2
    Modérateur
    Avatar de sabotage
    Homme Profil pro
    Inscrit en
    Juillet 2005
    Messages
    29 208
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Juillet 2005
    Messages : 29 208
    Points : 44 155
    Points
    44 155
    Par défaut
    Un très bon article qui pourra peut être te répondre :
    http://julien-pauli.developpez.com/t...als/variables/
    N'oubliez pas de consulter les FAQ PHP et les cours et tutoriels PHP

  3. #3
    Membre expert
    Avatar de s.n.a.f.u
    Homme Profil pro
    Développeur Web
    Inscrit en
    Août 2006
    Messages
    2 760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Août 2006
    Messages : 2 760
    Points : 3 545
    Points
    3 545
    Par défaut
    Je vais répondre un peu à côté : tu veux économiser la mémoire pour ne pas avoir deux instances d'une même chaîne.
    Ne suffirait-il pas de supprimer la variable chaîne une fois qu'elle est stockée dans le tableau ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    $str = 'foo';
    $vars[] = $str;
    unset($str);
    Tu t'éviteras des problème de fuites mémoires avec les références...
    • Avant de poser une question, n'hésitez pas à chercher dans la FAQ et les forums
    • Merci d'utiliser les balises de code (# dans l'éditeur)
    • N'oubliez pas de vous servir des boutons , et

    S.N.A.F.U

  4. #4
    Membre émérite Avatar de Djakisback
    Profil pro
    Inscrit en
    Février 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 2 022
    Points : 2 273
    Points
    2 273
    Par défaut
    Merci pour vos message

    L'article est effectivement très intéressant. Je me doutais qu'il y avait un mécanisme d'optimisation interne.

    Il mérite une 2e relecture de ma part mais il y a quelque chose qui me pose problème dans cet article.
    En utilisant la méthode décrite dans mon premier post (en php 5.2.1) je passe d'une consommation de 6Mo à 4Mo. Ce qui me semble être la preuve que tous les string similaires sont stockés dans des zones mémoires bien distinctes. D'un autre côté j'utilise memory_get_usage() pour les tests, ce qui n'est peut-être pas très fiable (mais j'en ai quand même fait un paquet et obtenus toujours les mêmes résultats)

    Je charge un tableau multi-dimensionnel qui est membre d'un objet.
    Peut-être que le COW ne fonctionne pas à l'intérieur d'un objet ou bien mal, en 5.2.1.

    A chaque nouvelle entrée du tableau s'il contient déjà un string similaire au nouveau à ajouter, je stocke une référence vers l'ancien plutôt que de stocker le nouveau et donc, comme je le disais j'ai un gain d'environ 2Mo.

    Je ne sais pas encore si j'ai besoin de la modification simultanée de toutes les entrées égales du tableau, il faut que je réfléchisse à la question (sinon effectivement je pourrais aussi libérer les variables au fur et à mesure).

    Sinon, l'article ne semble pas expliquer le comportement du var_dump() : le fait que l'indice 0 du tableau pointe vers une référence quand je passe une référence vers l'indice 0 à l'indice 1.

    s.n.a.f.u, merci pour ton idée mais les variables n'existent déjà pas en tant que telles car il s'agit de résultats SQL retournés par des mysql_result() ou autres. Je pourrais effectivement libérer les ressources MySQL avant la fin du script mais c'est surtout le tableau multi-dimensionnel qui bouffe la ram. Il me semble d'ailleurs que la conso SQL n'est pas prise en compte dans la taille maximum allouée par script.

    Il faudrait aussi que je teste XDebug.

    [EDIT]
    Bon après de nouveaux tests sous 5.3 tout semble normal (comme décrit dans l'article ).
    Je vais tenter sous 5.2.1 pour voir s'il y a un bug et investiguer pour comprendre pourquoi j'ai un gain dans le code réel (qui doit venir d'ailleurs a priori).
    Le code de test qui retourne les résultats attendus :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    $usedMemory = memory_get_usage();
     
    for($i = 0; $i < 999999; $i++)    {
        $array[] = "azeazeazeazeazeaze";
    }
    // used memory: 110.81178283691 Mo
     
    $str = "azeazeazeazeazeaze";
    for($i = 0; $i < 999999; $i++)    {
        $array[] = $str;
    }
    // used memory: 49.776741027832 Mo
     
    $array[0] = "azeazeazeazeazeaze";
    for($i = 0; $i < 999999; $i++)    {
        $array[] =& $array[0];
    }
    // used memory: 49.776748657227 Mo
     
     
     
    for($i = 0; $i < 999999; $i++)    {
        $array[0][] = "azeazeazeazeazeaze";
    }
    // used memory: 110.81195068359 Mo
     
    $str = "azeazeazeazeazeaze";
    for($i = 0; $i < 999999; $i++)    {
        $array[0][] = $str;
    }
    // used memory: 49.776908874512 Mo
     
    $array[0][0] = "azeazeazeazeazeaze";
    for($i = 0; $i < 999999; $i++)    {
        $array[0][] =& $array[0][0];
    }
    // used memory: 49.776908874512 Mo
     
    class Test    {
        var $array;
     
        function Test()    {
        }
     
        function t1()    {
            for($i = 0; $i < 999999; $i++)    {
                $this->array[] = "azeazeazeazeazeaze";
            }
        }
     
        function t2()    {
            $str = "azeazeazeazeazeaze";
            for($i = 0; $i < 999999; $i++)    {
                $this->array[] = $str;
            }    
        }
     
        function t3()	{
            $this->array[0] = "azeazeazeazeazeaze";
            for($i = 0; $i < 999999; $i++)	{
                $this->array[] =& $this->array[0];
            }
        }
    }
     
    $test = new Test();
    $test->t1();
    // used memory: 110.81188964844 Mo
    $test->t2();
    // used memory: 49.776802062988 Mo
    $test->t3();
    // used memory: 49.776802062988 Mo
     
     
     
    echo 'used memory: '.((memory_get_usage() - $usedMemory) / 1024 / 1024).' Mo';
    En tout cas merci pour la "remise dans le droit chemin"
    Vive les roues en pierre

  5. #5
    Membre émérite Avatar de Djakisback
    Profil pro
    Inscrit en
    Février 2005
    Messages
    2 022
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2005
    Messages : 2 022
    Points : 2 273
    Points
    2 273
    Par défaut
    Bon je continue quand même dans ma lancée, même si le code que je vais poster ne risque pas d'inspirer grand monde
    J'ai une méthode setField() dans une classe qui stocke une valeur dans un tableau multi-dimensionnel. Il y a bel et bien un gain de mémoire en stockant des références vers d'autres strings à la main plutôt que directement la valeur elle-même. Dans les méthodes ci-dessous, je n'agis que sur le tableau membre de la classe donc le gain ne semble pas pouvoir venir d'ailleurs et cela va à l'encontre du mécanisme de Copy On Write décrit dans l'article, ce qui me trouble fortement. Le gain est proportionnel à la taille/diversité du tableau, logique.
    La méthode de base :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    	function setField($name, $value = null, $index = 0)	{
    		$this->fields[$name][$index] = $value;
    	}
    La méthode qui parcourt à chaque fois le tableau et stocke une référence plutôt que le string lui-même :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    	function setField($name, $value = null, $index = 0)	{
     
    		// Teste si la valeur du champ existe déjà, si oui on passe une référence plutôt que de doublonner la valeur
    		// TODO : DEbug : Version avec string pointant vers références de string déjà existants
    		$done = false;
    		if($value != null && isset($this->fields[$name]))	{
    			for($i = 0, $c = count($this->fields[$name]); $i < $c && $done == false; $i++)	{
    				if($this->fields[$name][$i] == $value)	{
    					$this->fields[$name][$index] =& $this->fields[$name][$i];
    					$done = true;
    				}
    			}
    		}
    		if($done == false)	{
    			$this->fields[$name][$index] = $value;
    		}
    	}
    Quelques résultats avec la méthode 1 et la méthode 2 avec 2 jeux de données différents :

    //test1
    méthode 1 : 6 Mo
    méthode 2 : 4 Mo

    //test2
    méthode 1 : 14 Mo
    méthode 2 : 10 Mo
    Sinon, si quelqu'un a des infos sur ce fameux résultat du var_dump() du premier post, ça m'intéresse bien.
    Je vais de ce pas télécharger XDebug
    Vive les roues en pierre

Discussions similaires

  1. Demande d explication sur protocole FTP
    Par Mr_Chut dans le forum Réseau
    Réponses: 1
    Dernier message: 04/05/2007, 18h47
  2. Réponses: 4
    Dernier message: 09/10/2006, 22h12
  3. Réponses: 3
    Dernier message: 27/09/2006, 13h11
  4. [C#] demande d'explication sur un sample msdn
    Par legillou dans le forum Windows Forms
    Réponses: 2
    Dernier message: 27/06/2006, 17h01
  5. [final]demande d'explication sur ce mot-clé
    Par Invité dans le forum Langage
    Réponses: 10
    Dernier message: 18/04/2006, 11h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo