Comment fonctionne la mémoire PHP

j'ai toujours entendu et cherché de nouveaux php 'bonnes pratiques d'écriture', par exemple: il est préférable (pour la performance) de vérifier si la clé de tableau existe plutôt que de chercher dans le tableau, mais il semble aussi préférable pour la mémoire aussi:

en Supposant que nous avons:

$array = array
(
    'one'   => 1,
    'two'   => 2,
    'three' => 3,
    'four'  => 4,
);

cela affecte 1040 octets de mémoire,

et

$array = array
(
    1 => 'one',
    2 => 'two',
    3 => 'three',
    4 => 'four',
);

nécessite 1136 octets

je comprends que l' key et value sûrement aura un mécanisme de stockage différent, mais s'il vous plaît pouvez-vous réellement m'indiquer le principe, comment ça marche?

exemple 2 (pour @teuneboon):

$array = array
(
    'one'   => '1',
    'two'   => '2',
    'three' => '3',
    'four'  => '4',
);

1168 octets

$array = array
(
    '1' => 'one',
    '2' => 'two',
    '3' => 'three',
    '4' => 'four',
);

1136 octets

consommer de même de la mémoire:

  • 4 => 'four',
  • '4' => 'four',
22
demandé sur George Garchagudashvili 2014-08-01 17:27:12

4 réponses

Note, la réponse ci-dessous est applicable pour PHP avant à la version 7 comme dans PHP 7 des changements majeurs ont été introduits qui impliquent également des structures de valeurs.

TL;DR

votre question n'est pas en fait à propos de "comment la mémoire fonctionne en PHP" (ici, je suppose, vous vouliez dire "allocation de mémoire"), mais à peu près "comment les tableaux en PHP" - et ces deux questions sont différentes. Pour résumer ce qui est écrit ci-dessous:

  • les tableaux PHP ne sont pas des" tableaux " au sens classique du terme. Ils sont de hachage cartes
  • Hash-map for PHP array a une structure spécifique et utilise de nombreuses choses de stockage supplémentaires, telles que des liens internes pointeurs
  • Hash-map items pour PHP hash-map utilisez également des champs supplémentaires pour stocker l'information. Et-oui, non seulement les clés de chaîne / entier importe, mais aussi ce qui sont des chaînes eux-mêmes, qui sont utilisés pour vos clés.
  • Option avec les touches string dans votre cas "gagnera" en termes de quantité de mémoire parce que les deux options seront hachées dans ulong (non signé long) touches hachage-carte, donc la différence réelle sera dans les valeurs, où l'option chaîne de caractères-clés a des valeurs entières (fixe-longueur), tandis que l'option nombre entier-clés A chaînes de caractères (la longueur dépendante de la marque) valeurs. Mais qui peut ne pas toujours être vrai en raison de collisions possibles.
  • touches"String-numeric", telles que '4', sera traité comme des clés entières et traduit en résultat de hachage entier comme il a été clé entière. Ainsi,'4'=>'foo' et 4 => 'foo' sont les mêmes choses.

Aussi, remarque importante: les images ici sont la propriété de PHP internals book

Hash-carte pour les tableaux PHP

tableaux PHP et tableaux C

vous devez réaliser une chose très importante: PHP est écrit en C, où des choses comme" associative array " n'existent tout simplement pas. Donc, en C "array" est exactement ce qu'est "array" - c'est-à-dire qu'il s'agit juste d'une zone consécutive en mémoire à laquelle on peut accéder par un consécutive décalage. Vos "clés" peuvent être seulement numériques, entières et seulement consécutives, à partir de zéro. Vous ne pouvez pas avoir, par exemple, 3,-6,'foo' comme vos "clés" là.

donc pour implémenter des tableaux, qui sont en PHP, il y a l'option hash-map, elle utilise fonction de hachage hash vos clés et les transformer en entiers, qui peut être utilisé pour les c-arrays. Cette fonction, cependant, ne sera jamais en mesure de créer un bijection entre les touches string et leurs résultats de hachage integer. Et il est facile de comprendre pourquoi: parce que cardinalité des cordes ensemble est beaucoup, beaucoup plus grande que la cardinalité de l'ensemble d'entiers. Illustrons par un exemple: nous allons recompter toutes les chaînes, jusqu'à la longueur 10, qui n'ont que des symboles alphanumériques (so,0-9,a-z et A-Z, total 62): c'est 62 10 total des chaînes de caractères possible. C'est autour de 8,39 E + 17. Comparez-le à environ 4E+9 que nous avons pour le type entier non signé (long entier, 32 bits) et vous obtiendrez l'idée-il y aura collisions.

PHP hash-map keys & collisions

maintenant, pour résoudre les collisions, PHP va simplement placer les éléments, qui ont la même fonction de hachage, dans une liste liée. Donc, Hachette-carte ne serait pas juste " liste de hachage elements", mais à la place il stockera des pointeurs vers des listes d'éléments (chaque élément dans une certaine liste aura la même clé de fonction de hachage). Et c'est là que vous avez le point à la façon dont il affectera l'allocation de mémoire: si votre tableau a des clés de chaîne, qui n'ont pas entraîné de collisions, alors aucun pointeur supplémentaire à l'intérieur de cette liste serait nécessaire, donc la quantité de mémoire sera réduite (en fait, c'est un très petit overhead, mais, puisque nous parlons de précis allocation de mémoire, cela devrait être prises en considération). Et, de la même façon, si vos clés de chaîne de caractères résulteront en de nombreuses collisions, alors plus de pointeurs supplémentaires seront créés, donc la quantité totale de mémoire sera un peu plus importante.

pour illustrer ces relations à l'intérieur de ces listes, voici un graphique:

enter image description here

ci-dessus, il y a la façon dont PHP résoudra les collisions après avoir appliqué la fonction de hachage. Donc une de vos questions se trouve ici, pointeurs à l'intérieur des listes de résolution de collision. En outre, les éléments des listes liées sont généralement appelés seaux et le tableau, qui contient des pointeurs vers les chefs de ces listes est interne appelé arBuckets. En raison de l'optimisation de la structure (donc, pour faire des choses comme la suppression d'élément, plus rapide), l'élément de liste réelle a deux pointeurs, l'élément précédent et l'élément suivant - mais c'est seulement fera la différence dans la quantité de mémoire pour non-collision/collision tableaux un peu plus large, mais ne changera pas le concept m'.

une autre liste: order

supporter pleinement les tableaux comme ils sont en PHP, il est également nécessaire de maintenir , qui est réalisé avec une autre liste interne. Chaque élément de tableaux est un membre de cette liste. Cela ne fera pas de différence en termes d'allocation de mémoire, puisque dans les deux options cette liste devrait être maintenue, mais pour une image complète, je mentionne cette liste. Voici le graphique:

enter image description here

pListLast et pListNext, les pointeurs de la tête et de la queue de la liste d'ordre sont stockés. Encore une fois, ce n'est pas directement lié à votre question, mais plus loin je vais jeter la structure interne du seau, où ces pointeurs sont présents.

Element Array from inside

maintenant nous sommes prêts à regarder dans: qu'est-ce que l'élément array, donc,seau:

typedef struct bucket {
    ulong h;
    uint nKeyLength;
    void *pData;
    void *pDataPtr;
    struct bucket *pListNext;
    struct bucket *pListLast;
    struct bucket *pNext;
    struct bucket *pLast;
    char *arKey;
} Bucket;

ici nous sont:

  • h est une valeur entière (ulong) de la clé, c'est un résultat de la fonction de hachage. Pour les touches entières c'est juste même que la clé elle-même (fonction de hachage renvoie lui-même)
  • pNext/pLast sont des pointeurs à l'intérieur de collision-résolution liée liste
  • pListNext/pListLast sont des pointeurs à l'intérieur de l'ordre-résolution liée liste
  • pData est un pointeur vers la valeur stockée. En fait, la valeur n'est pas la même inséré au tableau la création, c'est copier, mais, pour éviter les frais généraux inutiles, PHP utilise pDataPtr (pData = &pDataPtr)

de ce point de vue, vous pouvez obtenir la prochaine chose où la différence est: puisque la clé string sera hachée (ainsi, h toujours ulong et, par conséquent, la même taille), ce sera une question de ce qui est stocké dans les valeurs. Ainsi, pour votre tableau de clés de chaîne, il y aura des valeurs entières, tandis que pour le tableau de clés de chaîne, il y aura des valeurs de chaîne, et cela fait la différence. Toutefois - non, il n'est pas de la magie: vous ne pouvez pas "sauvegarder la mémoire" en stockant les clés string de cette façon tout le temps, parce que si vos clés sont grandes et il y en aura beaucoup, cela provoquera des collisions au-dessus de la tête (Eh bien, avec une très forte probabilité, mais, bien sûr, pas garanti). Il ne "fonctionnera" que pour des chaînes courtes arbitraires, ce qui ne provoquera pas beaucoup de collisions.

la table de hachage elle-même

on a déjà parlé d'éléments (seaux) et leur structure, mais il y a aussi la table de hachage elle-même, qui est, en fait, la structure de données de tableau. Donc, il est appelé _hashtable:

typedef struct _hashtable {
    uint nTableSize;
    uint nTableMask;
    uint nNumOfElements;
    ulong nNextFreeElement;
    Bucket *pInternalPointer;   /* Used for element traversal */
    Bucket *pListHead;
    Bucket *pListTail;
    Bucket **arBuckets;
    dtor_func_t pDestructor;
    zend_bool persistent;
    unsigned char nApplyCount;
    zend_bool bApplyProtection;
#if ZEND_DEBUG
    int inconsistent;
#endif
} HashTable;

Je ne vais pas décrire tous les champs, puisque j'ai déjà fourni beaucoup d'information, qui est seulement liée à la question, mais je vais décrire cette structure brièvement:

  • arBuckets est-ce qui a été décrit ci-dessus, les seaux de stockage,
  • pListHead/pListTail sont des pointeurs à l'ordre-résolution liste
  • nTableSize fixe la taille de la table de hachage. Et cela est directement lié à l'allocation de la mémoire: nTableSize est toujours puissance de 2. Ainsi, peu importe si vous avez 13 ou 14 éléments dans le tableau: la taille réelle sera 16. Prendre à compte lorsque vous souhaitez estimer la taille du tableau.

Conclusion

il est très difficile de prédire si un tableau sera plus grand qu'un autre dans votre cas. Oui, il y a des lignes directrices qui sont structure, mais si les clés string sont comparables par leur longueur à des valeurs entières (comme 'four','one' dans votre échantillon) - la différence réelle sera dans des choses telles que-combien de collisions se sont produites, combien d'octets ont été alloués pour sauver la valeur.

mais le choix de la structure appropriée devrait être une question de sens, pas de mémoire. Si votre intention est de construire les données indexées correspondantes, alors le choix est toujours évident. Post ci-dessus n'est qu'environ un but: montrer comment les tableaux fonctionnent réellement dans PHP et où vous pouvez trouver la différence dans l'allocation de mémoire dans votre échantillon.

vous pouvez aussi vérifier l'article à propos des tableaux et des tables de hachage en PHP: c'est tables de hachage en PHP par PHP internals book: j'ai utilisé quelques graphiques de là. Aussi, pour réaliser, comment les valeurs sont allouées en PHP, cochez Structure zval article, il peut vous aider à comprendre, ce qui sera des différences entre les chaînes et l'attribution des entiers pour les valeurs de vos tableaux. Je n'ai pas inclus des explications de celui - ci ici, puisque beaucoup plus important pour moi-est de montrer la structure de données de tableau et ce qui peut être la différence dans le contexte de clés de chaîne/clés entières pour votre question.

22
répondu Alma Do 2015-08-10 07:58:53

bien que les deux tableaux soient accessibles d'une manière différente (c.-à-d. via une chaîne de caractères ou une valeur entière), le modèle de mémoire est essentiellement similaire.

c'est parce que l'allocation de chaîne de caractères se produit soit dans le cadre de attribut création ou quand une nouvelle clé de tableau doit être attribuée; la petite différence étant que les indices numériques ne nécessitent pas une structure zval entière, parce qu'ils sont stockés comme un (non signé) long.

Les différences observées dans l'allocation de la mémoire sont si minimes qu'ils peuvent être largement attribués à l'imprécision de memory_get_usage() ou les allocations dues à la création d'un seau supplémentaire.

Conclusion

comment vous voulez utiliser votre tableau doit être le principe directeur dans le choix comment il devrait être indexé; la mémoire ne devrait devenir une exception à cette règle que lorsque vous vous épuisez.

3
répondu Ja͢ck 2014-08-04 09:00:02

à Partir de PHP manuel de Collecte des Ordures http://php.net/manual/en/features.gc.php

gc_enable(); // Enable Garbage Collector
var_dump(gc_enabled()); // true
var_dump(gc_collect_cycles()); // # of elements cleaned up
gc_disable(); // Disable Garbage Collector

PHP ne renvoie pas très bien la mémoire libérée; son utilisation principale en ligne ne l'exige pas et un ramassage efficace des ordures prend du temps pour fournir la sortie; lorsque le script se termine, la mémoire va être retournée de toute façon.

la collecte des ordures se produit.

  1. quand vous le dites à

    int gc_collect_cycles ( void )

  2. Lorsque vous quittez une fonction

  3. quand le script se termine

meilleure compréhension de la collecte de déchets de PHP à partir d'un hôte web, (aucune affiliation). http://www.sitepoint.com/better-understanding-phps-garbage-collection/

si vous considérez byte by byte comment les données sont réglées en mémoire. Différents ports vont affecter ces valeurs. La performance CPUs de 64 bits est la meilleure lorsque data s'assoit sur le premier bit d'un mot 64bit. Pour la performance max un binaire spécifique ils attribueraient le début d'un bloc de mémoire sur le premier bit, laissant jusqu'à 7 octets inutilisés. Ce truc spécifique au CPU dépend du compilateur qui a été utilisé pour compiler Le PHP.EXE. Je ne peux proposer aucun moyen de prédire l'utilisation exacte de la mémoire, étant donné qu'elle sera déterminée différemment par différents compilateurs.

Alma Do, post va aux spécificités de la source qui est envoyée au compilateur. Quel les requêtes des sources PHP et le compilateur sont optimisés.

en Regardant les exemples que vous avez posté. Lorsque la clé est une lettre ascii, ils prennent 4 octets (64 bits) de plus par entrée ... cela me suggère, (en supposant qu'il n'y ait pas de trous de mémoire ou d'ordures, ect), que les clés ascii sont supérieures à 64 bits, mais que les clés numériques correspondent à un mot de 64 bits. Il me suggère d'utiliser un ordinateur 64bit et votre PHP.exe est compilé pour les CPU à 64 bits.

3
répondu Wayne 2014-10-20 13:18:13

les tableaux en PHP sont implémentés sous forme de hashmaps. Ainsi, la longueur de la valeur que vous utilisez pour la clé a peu d'impact sur l'exigence de données. Dans les versions plus anciennes de PHP, il y a eu une dégradation significative de la performance avec les grands tableaux puisque la taille du hachage a été fixée à la création du tableau - lorsque les collisions commenceront à se produire, alors un nombre croissant de valeurs de hachage se transformera en listes de valeurs liées qui devront ensuite être recherchées plus avant (avec un algorithme O(n)) au lieu d'une seule valeur, mais plus récemment le hachage semble soit utiliser une taille par défaut beaucoup plus grande ou est redimensionné dynamiquement (il fonctionne Juste - Je ne peux pas vraiment être ennuyé lisant le code source).

sauver 4 octets de vos scripts n'empêchera pas Google de passer des nuits blanches. Si vous écrivez du code qui utilise de grands tableaux (où les économies peuvent être plus significatives) vous le faites probablement mal - le temps et les ressources nécessaires pour remplir le tableau pourraient être mieux dépensés ailleurs (comme indexé stockage.)

1
répondu symcbean 2014-08-01 14:12:46