Précédent   Forum des professionnels en informatique > PHP > Langage > Syntaxe
Syntaxe Forum d'entraide sur la syntaxe de PHP et la POO. Avant de poster -> FAQ syntaxe, Cours d'initiation et cours de POO
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 25/03/2011, 15h50   #1
Membre Expert
 
Avatar de Djakisback
 
Inscription : février 2005
Messages : 1 791
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 1 791
Points : 1 681
Points : 1 681
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 :
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 :

Citation:
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 :
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 :

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

Citation:
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 ^_^)
Djakisback est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 25/03/2011, 16h30   #2
Modérateur
 
Avatar de sabotage
 
Homme Vincent
Inscription : juillet 2005
Messages : 14 929
Détails du profil
Informations personnelles :
Nom : Homme Vincent

Informations forums :
Inscription : juillet 2005
Messages : 14 929
Points : 16 381
Points : 16 381
Un très bon article qui pourra peut être te répondre :
http://julien-pauli.developpez.com/t...als/variables/
sabotage est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 25/03/2011, 17h19   #3
Modérateur
 
Avatar de s.n.a.f.u
 
Homme
Développeur Web
Inscription : août 2006
Messages : 2 700
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 37
Localisation : France, Loire Atlantique (Pays de la Loire)

Informations professionnelles :
Activité : Développeur Web

Informations forums :
Inscription : août 2006
Messages : 2 700
Points : 3 357
Points : 3 357
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 :
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)
  • Si votre problème est réglé, merci d'utiliser le bouton
S.N.A.F.U
s.n.a.f.u est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 25/03/2011, 18h04   #4
Membre Expert
 
Avatar de Djakisback
 
Inscription : février 2005
Messages : 1 791
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 1 791
Points : 1 681
Points : 1 681
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 :
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"
Djakisback est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 28/03/2011, 14h39   #5
Membre Expert
 
Avatar de Djakisback
 
Inscription : février 2005
Messages : 1 791
Détails du profil
Informations forums :
Inscription : février 2005
Messages : 1 791
Points : 1 681
Points : 1 681
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 :
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 :
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 :

Citation:
//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
Djakisback est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 21h59.


 
 
 
 
Partenaires

Hébergement Web