Page 7 sur 7

Re: Un démineur en python pour la NumWorks

Message non luPosté: 12 Avr 2022, 12:18
de cent20
critor a écrit:Mais sinon oui, ta liste de 10 listes de 15 entiers chacune coûte cher, c'est un ajout significatif au reste.
J'estime :
  • 64 octets pour la création de chaque liste, soit 64*11=704 octets
  • 8 octets pour chaque ajout d'élément à ces listes, soit 150*8+10*8=1280 octets
  • 28 octets pour la création de chaque entier associé à ces éléments, soit 150*28=4200 octets
Nous en sommes à 6184 octets, soit déjà 37,74% de la capacité du tas de la NumWorks rien que pour ta structure de données.
Donc rien d'étonnant aux erreurs de mémoire que tu rencontres en rajoutant des fonctions travaillant sur cette structure.


Je déterre ce message. 2 ans se sont écoulés, j'ai appris beaucoup depuis, mais cette réponse reste ma référence quand je cherche à déterminer les limites concernant l'utilisation de la mémoire sur la NumWorks.

Quand je teste ce script https://my.numworks.com/python/cent20/m ... _list_rand j'obtiens un 4096, donc 4096 entiers aléatoires stockés en mémoire avant saturation. Cette valeur est identique sur la calculatrice et sur l'émulateur.

4096 * ( 8 + 26 ) = 139 264 octets, autrement dit plus de 4 fois plus que la mémoire accessible indiquée via le test de critor : https://workshop.numworks.com/python/andreanx/mem

J'en viens à douter de l'évaluation 8 + 28 octet.

Re: Un démineur en python pour la NumWorks

Message non luPosté: 27 Avr 2022, 14:27
de parisse
Lorsqu'on ajoute un element a une liste dont la capacite est atteinte, en general une nouvelle liste de capacite 2* plus grande est allouee, et la liste initiale est recopiee dedans. Si le processus de doublement marche avec 2048 petits entiers et ne marche pas avec 4096, pour 32K de heap, on en deduit que la taille de representation d'un entier est <=32*1024/2048/3 et >=32*1024/4096/3, donc comprise entre 2.6 et 5.3 octets. Donc c'est tres vraissemblablement 4 octets/entier + une valeur fixe pour la liste. Sur architecture 32 bits, sizeof(mp_obj_t) devrait etre de 4, sauf si le flag de compilation MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D, donc mp_obj_t doit encoder directement l'entier.

Re: Un démineur en python pour la NumWorks

Message non luPosté: 27 Avr 2022, 14:31
de parisse
Je confirme, un petit entier est directement encode dans un mp_obj_t:
Code: Tout sélectionner
static inline bool mp_obj_is_small_int(mp_const_obj_t o)
    { return ((((mp_int_t)(o)) & 1) != 0); }
#define MP_OBJ_SMALL_INT_VALUE(o) (((mp_int_t)(o)) >> 1)
#define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 1) | 1))

Re: Un démineur en python pour la NumWorks

Message non luPosté: 27 Avr 2022, 14:50
de parisse
Bon, j'ai repondu un peu vite, car MICROPY_OBJ_REPR_D est defini. Donc les mp_obj_t et donc les petits entiers sont sur 8 octets de meme que les flottants. Du coup, ca parait etrange de pouvoir atteindre 4096 entiers dans une liste avec un tas de 32K.

Re: Un démineur en python pour la NumWorks

Message non luPosté: 27 Avr 2022, 17:53
de cent20
Bon je n'ai pas tout compris mais ça me semble très intéressant à creuser.

Code original de mon test primitif : (copié ici pour archivage)

Code: Tout sélectionner
from random import randint
tab = []
for i in range(6666):
  try:
    tab.append(randint(1,999999))
  except:
    print(i, "enregistrement dans la \nliste")
    break


Affiche :
4096 enregistrement dans la liste

Je vais tester avec d'autres valeurs que 999999 pour voir. :)

Re: Un démineur en python pour la NumWorks

Message non luPosté: 27 Avr 2022, 18:04
de cent20
Deuxième essai

Code: Tout sélectionner
from random import randint

def fill_memory(n):
  tab = []
  i = 0
  while True:
    try:
      tab.append(randint(1,n))
      i += 1
    except:
      print(i, "val entre 1 et",n)
      break
   
fill_memory(2**2) #4096
fill_memory(2**5) #4096
fill_memory(2**8) #4096
fill_memory(2**16) #4096
fill_memory(2**32) #0
fill_memory(2**64) #0


randint(1,2**32) ne veut pas marcher :D
"OverflowError : overflow converting long int to machine word"

Re: Un démineur en python pour la NumWorks

Message non luPosté: 27 Avr 2022, 18:14
de cent20
Bon 32 ko / 4096 ça donne exactement 8 octet par valeur, mais ça ne tient pas compte de la fonction, des autres objets, donc je tablerais sur 4 octets par valeur stockée et ainsi on occuperait au maximum 16 ko du tas. Il me semble qu'il est fragmenté, et que le plus gros segment fait 16 ko, soit exactement 4 * 4096...

Ceci dit, ce ne sont que des conjectures.

Re: Un démineur en python pour la NumWorks

Message non luPosté: 27 Avr 2022, 18:20
de cent20
J'ai trouvé un truc très interéssant. randint(1,2**32 ne voulant pas fonctionner, j'ai truqué le tirage.

Code: Tout sélectionner
def fill_memory_bis(n):
  tab = []
  i = 0
  while True:
    try:
      tab.append(2**32+randint(1,n))
      i += 1
    except:
      print(i, "val entre 1 et",n)
      break 


Ainsi, mes entiers sont tous supérieur à 2**32, et ne peuvent être stockés sur 32 bits (donc 4 octets).

La calculatrice n'accepte alors que 256 valeurs pour fill_memory_bis(2**16)

En reprenant ma théorie précédente, à savoir "seul le plus gros segment de mémoire est exploité (16 ko)" cela donne un codage sur 64 octets cette fois ci.

Re: Un démineur en python pour la NumWorks

Message non luPosté: 27 Avr 2022, 19:29
de parisse
Bon, ma propre config de micropython est probablement differente de celle utilisee aujourd'hui par Epsilon.
Donc dans la config d'Epsilon, les entiers <2^31 sont representes dans un mp_obj_t sur 4 octets (et au-dela on a des entiers longs). Mais il y a un autre prix a payer c'est que les flottants au format double ne peuvent pas etre immediats, donc ils doivent occuper au moins 12 octets (s'il y a un pool de double), plus probablement 16 octets.
Maxi 256 entiers longs ca parait peu.