Occupation RAM des variables objet

Je viens de déclarer un bug sur la (mauvaise) purge des variables objets qui ne se fait pas sur MacOS avec les versions 64 bits, ce qui m’a amené à me poser la question suivante :

Comment déterminer les besoins en mémoire vive de variables objets, qui peuvent avoir des ramifications complexes?

Naïvement, j’ai pensé que cela pouvait être la somme des types habituels de variables qui s’additionnait
(Reel 10 octets, entier 2 octets, texte 2o/caractères (ou 1?), etc…)

Et puis, j’ai testé un bon gros fichier json qui faisait 21,7 Mo sur disque avec uniquement des objets comme ceci :
{propriete1:{},“Propriété2”:{}}

Pour le test, mes objets valeurs étaient vides et les noms de mes propriétés faisaient tous 19 caractères
Mon fichier json fait 869342 propriétés avec autant d’objets valeurs vides.

L’occupation RAM de ce fichier chargé en json parse a été de 478 Mo environ (cela peut varier).

En passant à une forme de fichier (même nombre de propriétés) ou je remplace le sous-objet vide {} par une chaine vide “”, {propriete1:"",“Propriété2”:"", etc} l’occupation mémoire n’est pus que de 274 Mo environ.

En divisant cette différence par mon nombre de propriétés, j’obtiens 241 octets de ram occupée par objet vide, ce qui me parait très important.
Je précise que la longueur des noms de propriétés restant identique, la différence ne doit s’opérer qu’entre le type chaine, et le type objet.

Quelqu’un pourrait-il me confirmer la justesse du raisonnement, ou à défaut m’indiquer comment l’on procède pour anticiper les besoins en ram de ces variables.

Le test a été réalisé sur MacOS 10.13.5 & 16R6.221608 sur un iMac Pro 32Go Ram.

Merci

Anyone?

Ta démarche est intéressante. Cela dit, je pense qu’il y a pas mal d’écueils à cette prévision :

  • un objet, ça ressemble furieusement à un pointeur car je peux faire MaMethode($objet) et récupérer $objet modifié en sortie. Donc il y a la taille du “truc qui permet de pointer” (ou de référencer) en plus
  • tu as la notion de refcount, deux objet peuvent référencer la même chose, pour autant cette chose n’existe qu’une fois, seule la ref est en double
  • un objet peut englober un pointeur : faut-il prendre en compte le pointeur, la chose pointée, les deux ?
  • la structure de l’objet a certainement une incidence, un peu comme un DOM en xml : est-ce qu’un objet “à plat” et un objet “à tiroir”, qui ont en tout le même nombre de propriétés, pèsent la même chose ? J’en doute.
    etc.

$disMoiSiÇaChie:=OB occupation memoire($obj)
commande pas dans le manuel :wink:

Bonjour Arnaud,

Merci pour ta tentative :wink:

Je pense qu’il y a effectivement la notion de reference, mais il doit y avoir quelque part la notion de conteneur de données vers laquelle pointe la référence, et qui pourrait être “pesée”.

… Et comme tout ceci à l’air d’avoir une gourmandise mémoriphage, ton idée d’ OB Occupation memoire serait bonne à prendre…

Ceci étant dit, il doit bien y avoir une méthode de prévision

  • Attribut : nb caracteres * n octets + n octets ref
  • valeur : nb caracteres * n octets | poids entier | poids reel, etc.

… sauf que ça consomme vraiment un max

anyone 4D?

Bonjour,

Comment as tu estimé l’empreinte mémoire à 478 Mo ?

Bonjour,

Je ne l’ai pas estimée

Je l’ai constatée avec le contrôleur d’exécution…

Et on est très au dessus de la simple somme d’octets (valeurs)

: Stanislas DE

CLERMONT-TONNERRE

Je l’ai constatée avec le contrôleur d’exécution…
Tu peux expliciter ?
Complètement neophyte je suis…

PS : parce que si on peut mesurer la montée mémoire au fur et à mesure qu’on charge l’objet, il me semble que ça constitue une piste pour en déduire ce qui t’intéresse.

[]26197740;“Explorateur d’exécution”[/]

“Mémoire Physique utilisée”

Ca grimpe en même temps qu’on charge la variable

et ca de descend pas quand on la purge (sur Mac)

Ouille, c’est la grande patience, là. À touzazar, j’ai cherché une commande qui retourne cette valeur, mais rien de probant.
Sinon, après un test rapide, peut-être que le bon vieux VARIABLE VERS BLOB peut nous aider :
<code 4D>
C_OBJET($ob_o)
C_BLOB($_x)
$ob_o:=Créer objet
$ob_o.truc:=“a”
VARIABLE VERS BLOB($ob_o;$_x) //21 octets
$ob_o.machin:=“toto”
VARIABLE VERS BLOB($ob_o;$_x) //37 octets
$ob_o.bidule:=“anticonstitutionnellement”
VARIABLE VERS BLOB($ob_o;$_x) //74 octets
$ob_o.nombre1:=123
VARIABLE VERS BLOB($ob_o;$_x) //88 octets
$ob_o.nombre2:=123,456
VARIABLE VERS BLOB($ob_o;$_x) //106 octets
</code 4D>
à creuser…

: Arnaud DE MONTARD

à creuser…
creusons :

<code 4D>
C_OBJET($ob_o)
C_BLOB($_x)
C_ENTIER LONG($_l)
C_RÉEL($_r)
C_TEXTE($_t)
$ob_o:=Créer objet
$ob_o.long:=$_l
VARIABLE VERS BLOB($ob_o;$_x) //19 octets
$ob_o.reel:=$_r
VARIABLE VERS BLOB($ob_o;$_x) //28 octets
</code 4D>

et re creusons :

<code 4D>
C_OBJET($ob_o)
C_BLOB($_x)
C_ENTIER LONG($_l)
C_RÉEL($_r)
C_TEXTE($_t)
$_r:=Pi
$ob_o:=Créer objet
$ob_o.long:=$_l
VARIABLE VERS BLOB($ob_o;$_x) //19 octets
$ob_o.reel:=$_r
VARIABLE VERS BLOB($ob_o;$_x) //42 octets
</code 4D>

si se baser sur la taille du blob est une démarche fiable, ça dit déjà que :

  • la longueur du texte joue
  • un réel à 0 et un à ∏ n’occupent pas la même taille

Je ne sais trop que penser de cette approche.
La mémoire physique utilisée ne semble pas toujours correspondre seulement à la taille de l’objet chargé en mémoire.

J’ai fait le bout de code suivant et je regarde l’évolution de la mémoire physique utilisée à chaque alerte.
<code 4D>
C_BLOB($blob;$blob2)
C_ENTIER LONG($nb;$i)
C_OBJET($ob;$ob2)
C_TEXTE($texte;$texte2)
$nb:=Num(Demander(“Nombre de propriétés :”;“1 000 000”))
Si (OK=1)
ALERTE(“Avant construction.”)
Boucle ($i;1;$nb)
OB FIXER($ob;“p”+Chaîne($i;“0000000”);MAXLONG)
Fin de boucle
ALERTE(“Objet constitué.”)
$ob2:=OB Copier($ob)
ALERTE(“Objet copié.”)
VARIABLE VERS BLOB($ob;$blob)
ALERTE(“Blob constitué.”)
$blob2:=$blob
ALERTE(“Blob copié.”)
$texte:=JSON Stringify($ob)
ALERTE(“Texte constitué.”)
$texte2:=$texte
ALERTE(“Texte copié.”)
ALERTE("Taille blob : “+Chaîne(Taille BLOB($blob))+”\rTaille texte : "+Chaîne(Longueur($texte)))
Fin de si
</code 4D>
Plusieurs remarques :

  • La mémoire n’augmente pas de la même quantité de “Avant construction” à “Objet constitué” et de “Objet constitué” à “Objet copié”. L’objet chargé en mémoire doit pourtant occuper la même place. Il y a donc autre chose qui a été mis en mémoire dans le premier intervalle.
  • La mémoire n’augmente pas de la taille du blob entre “Objet copié” et “Blob constitué”. Le blob fait environ 20 Mo et la mémoire utilisée augmente de plus de 200 Mo ! La remarque est vrai aussi pour le texte.
  • La mémoire utilisée n’augmente pas entre “Blob constitué” et “Blob copié”, pareil pour le texte.

En revanche, pour le code suivant, on constate que la mémoire utilisée augmente bien d’une valeur proche de celle attendue :
<code 4D>
C_BLOB($blob)
ALERTE(“Avant”)
FIXER TAILLE BLOB($blob;10010241024)
ALERTE(“Après”)

</code 4D>

La méthode n’est donc pas fiable pour mesurer ce que tu souhaites.

: Stanislas CARON

J- La mémoire utilisée n’augmente pas entre “Blob constitué” et “Blob
copié”, pareil pour le texte.
Pour $texte2 en ligne 20, cela me semble logique s’il est refcounté. Il suffirait sans doute de faire :
$texte2:=$texte2+“xxx”
à la ligne suivante : si la mémoire diminue ça confirmerait le refcount car dans ce cas $texte et $texte2 ne peuvent plus référencer la même chose.
Pour le blob, je me souviens qu’en formation v11 Thibaud avait dit qu’images et textes étaient refcountés mais pas les blobs. Ça a peut-être changé depuis.

Sinon j’ai regardé :

• la taille des noms de propriétés :
<code 4D>
C_BLOB($size_x)
C_OBJET($ob_o)
$racineProp_t:=“a”
$ob_o:=Créer objet
TABLEAU ENTIER LONG($taille_a2l;2;0)
Boucle ($i_l;1;10)
$prop_t:=$racineProp_t*$i_l //longueur propriété ++
$ob_o[$prop_t]:=0
VARIABLE VERS BLOB($ob_o;$size_x)
AJOUTER À TABLEAU($taille_a2l{1};Taille BLOB($size_x)) //taille
AJOUTER À TABLEAU($taille_a2l{2};$taille_a2l{1}{$i_l}-$taille_a2l{1}{$i_l-1}) //∆ précédent
Fin de boucle
</code 4D>
quand $nomProp_t vaut :

  • “a”, le blob prend 1 octet de plus à chaque fois qu’on allonge la propriété
  • “à”, 2 octets
  • :mask:”, 4 octets (où l’on constate que je suis assez tordu pour tenter un emoji comme nom de propriété)

• la longueur d’un nombre entier
<code 4D>
C_BLOB($size_x)
C_OBJET($ob_o)
$racineProp_t:=“p”
$ob_o:=Créer objet
TABLEAU ENTIER LONG($taille_a2l;2;0)
Boucle ($i_l;1;9)
$prop_t:=$racineProp_t+Chaîne($i_l)
$ob_o[$prop_t]:=Ent(Num(“1”*$i_l))
VARIABLE VERS BLOB($ob_o;$size_x)
AJOUTER À TABLEAU($taille_a2l{1};Taille BLOB($size_x)) //taille
AJOUTER À TABLEAU($taille_a2l{2};$taille_a2l{1}{$i_l}-$taille_a2l{1}{$i_l-1}) //∆ précédent
Fin de boucle
</code 4D>
un octet par chiffre ajouté à l’entier.

• la longueur d’un nombre réel
<code 4D>
C_BLOB($size_x)
C_OBJET($ob_o)
$racineProp_t:=“p”
$ob_o:=Créer objet
TABLEAU ENTIER LONG($taille_a2l;2;0)
Boucle ($i_l;1;9)
$prop_t:=$racineProp_t+Chaîne($i_l)
$ob_o[$prop_t]:=Arrondi(1/3;$i_l)
VARIABLE VERS BLOB($ob_o;$size_x)
AJOUTER À TABLEAU($taille_a2l{1};Taille BLOB($size_x)) //taille
AJOUTER À TABLEAU($taille_a2l{2};$taille_a2l{1}{$i_l}-$taille_a2l{1}{$i_l-1}) //∆ précédent
Fin de boucle
</code 4D>
un octet par chiffre ajouté aux décimales.

Mais ça ne me dit toujours pas si blober une variable objet est une façon correcte d’en évaluer la taille.

D’autant qu’ayant fait quelques test allant dans ce sens, j’ai eu un résultat différent avec 2 octets par caractères quel qu’il soit (en v17 mac 64bits)

Et puis j’ai fait ce test avec un fichier json de 20mo (en pj zippée)
http://forums.4d.com/4DBB_Main/x_User/1256124/files/26212925.zip

LIRE STATISTIQUES MÉMOIRE(1;$tabNoms;$tabValeurs;$tabNombre)
$search:=Chercher dans tableau($tabNoms;“Used physical memory”)
Si ($search#-1)
$used_memory:=$tabValeurs{$search}
Fin de si
$a:=Ouvrir document("";Lire chemin accès)
Si (ok=1)
C_OBJET($mavar)
$mavar:=JSON Parse(Document vers texte(Document)) //je charge mon fichier dxe 20mo qui en prend 400 en ram
OB LIRE NOMS PROPRIÉTÉS($mavar;$tab_P)
Fin de si
LIRE STATISTIQUES MÉMOIRE(1;$tabNoms;$tabValeurs;$tabNombre)
$search:=Chercher dans tableau($tabNoms;“Used physical memory”)
Si ($search#-1)
$end_memory:=$tabValeurs{$search}
$delta:=$end_memory-$used_memory //Je vois ici le delta de ram “consommée”
Fin de si
TRACE
VARIABLE VERS BLOB($mavar;$blob) //Placée dans un blob, elle pèse 20Mo
TRACE
$mavar:=Créer objet //Si je la purge
LIRE STATISTIQUES MÉMOIRE(1;$tabNoms;$tabValeurs;$tabNombre)
$last_memory:=$tabValeurs{$search} //La ram n’est pas restituée
TRACE

Ca fait très mal en RAM

J’ai remonté l’absence de purge sur Mac en bug

Étonnant, Non?

: Stanislas DE

CLERMONT-TONNERRE

D’autant qu’ayant fait quelques test allant dans ce sens, j’ai eu un
résultat différent avec 2 octets par caractères quel qu’il soit (en
v17 mac 64bits)
emoji compris ? Sinon j’étais en v32bits, aucune idée de ce que ça change mais ça ne m’étonne pas trop.

Pourquoi purges-tu avec $mavar:=Créer objet ? Spontanément, j’opterais pour EFFACER VARIABLE($mavar)…

C’est une des tentatives

Normalement, la variable locale aurait du se purger à la sortie de la méthode, ce qui n’était pas le cas.

Donc j’ai essayé avec effacer variable, avec créer objet, avec $mavar:=null

Rien n’y fait.

Pour le double bit, j’ai remarqué que c’était également le cas pour les tableaux texte en 64 bits.

: Stanislas DE

CLERMONT-TONNERRE

Étonnant, Non?
Le mécanisme de nettoyage mémoire imaginé par Laurent Esnault a été évoqué par LR au summit. J’ai retenu que c’est vachement bien et que jamais je ne chercherai à comprendre.

: Stanislas DE

CLERMONT-TONNERRE

Pour le double bit, j’ai remarqué que c’était également le cas pour
les tableaux texte en 64 bits.
Peut-être une des raisons pour qu’Olivier Deschanels dise souvent que 32 et 64 ne sont pas de simples déclinaisons mais des versions aussi différentes qu’une mac l’est d’une win…