Épisode 5 - Python et tas (heap)
Nous en profitons de plus pour te réaliser le travail titanesque d'étendre les tests aux modèles plus anciens :
- toutes les calculatrices graphiques Texas Instruments (depuis la première TI-81 de 1990)
- toutes les calculatrices graphiques Casio à technologie Flash (depuis 2003)
- La mémoire de stockage comme son nom l'indique stocke physiquement tes scripts Python.
- La pile (stack) référence, à l'exécution, les objets Python créés. Sa capacité limite donc le nombre d'objets Python pouvant coexister simultanément en mémoire.
- Le tas (heap) stocke, à l'exécution, le contenu des objets Python créés. Il limite donc la taille globale utilisée pour les données de ces différents objets.
Aujourd'hui nous allons donc nous intéresser au heap. Cet espace est extrêmement important et surtout sur les plateformes nomades, car contrairement à d'autres langages les objets Python les plus simples ont le défaut d'être assez gros. Ce sera bien souvent le heap le facteur le plus limitant pour tes projets Python, d'où son caractère essentiel.
Le temps de construire notre protocole de test, concentrons-nous sur la TI-83 Premium CE Edition Python.
Nous avons justement la chance ici de disposer du module gc (garbage collector - ramasse miettes), avec plusieurs fonctions bien utiles :
gc.collect()
pour nettoyer le heap en supprimant les valeurs d'objets Python qui ne sont plus référencéesgc.mem_alloc()
pour connaître la consommation du heap en octetsgc.mem_free()
pour connaître l'espace heap disponible en octets
Exécutons donc le petit script suivant afin de découvrir la capacité heap Python de la TI-83 Premium CE Edition Python :
- Code: Tout sélectionner
import gc
a, f = gc.mem_alloc(), gc.mem_free()
(a, f, a + f)
Nous avons donc sur TI-83 Premium CE Edition Python une capacité heap de 19,968 Ko.
Mais lorsque l'on accède à l'environnement Python, nombre de choses sont initialisées et ce heap n'est pas vide. Plus que 17,104 Ko de libres.
Précisons que cet espace libre a de plus ici été amputé de par notre importation du module gc. Ce module n'étant hélas disponible que sur une minorité de Pythonnettes, il va nous falloir procéder autrement, surtout si l'on souhaite obtenir des mesures comparables.
Donnons quelques éléments de taille en mémoire d'objets Python usuels, du moins sur les plateformes 32 bits que sont nos calculatrices :
- pour un entier nul : 24 octets déjà...
- pour un entier court non nul (codable sur 31 bits + 1 bit de signe) : 28 octets
- pour un entier long :
- 28 octets
- + 4 octets pour chaque groupe de 30 bits utilisé par son écriture binaire au-delà des 31 bits précédents
- pour une chaîne :
- 49 octets
- + 1 octet par caractère
- pour une liste :
- 64 octets
- + 8 octets par élément
- + les tailles de chaque élément
Voici une fonction qui retourne la taille d'un objet selon ces règles :
- Code: Tout sélectionner
def size(o):
t = type(o)
s = t == str and 49 + len(o)
if t == int:
s = 24
while o:
s += 4
o >>= 30
elif t == list:
s = 64 + 8*len(o)
for so in o:
s += size(so)
return s
Nous allons donc tenter plutôt de remplir le heap avec plusieurs objets que nous allons faire grandir chacun son tour jusqu'à déclenchement d'une erreur, et retourner la capacité maximale que nous avons réussi à consommer. Voici donc un script en ce sens :
- Code: Tout sélectionner
def mem(v=1):
try:
l=[]
try:
l.append(0)
l.append(0)
l.append("")
l[2] += "x"
while 1:
try:
l[2] += l[2][l[1]:]
except:
if l[1] < len(l[2]) - 1:
l[1] = len(l[2]) - 1
else:
raise(Exception)
except:
if v:
print("+", size(l))
try:
l[0] += size(l)
except:
pass
try:
l[0] += mem(v)
except:
pass
return l[0]
except:
return 0
Exécuter ce script va ainsi nous permettre d'évaluer équitablement tous les modèles.
L'appel
mem(0)
semble marcher comme souhaité, retournant une valeur qui peut comme prévu légèrement dépasser les 17,104 Ko trouvés plus haut.Mais voilà autre petit problème, le résultat n'est pas toujours le même, dépendant en effet de l'état du heap lors de l'appel. Rien que sur les résultats ci-contre, nous avons une marge d'erreur de 1 à 2%.
C'est beaucoup, en tous cas suffisamment pour inverser injustement des modèles au classement. Or nous tenons à être aussi précis que possible, afin justement de produire un classement aussi équitable que possible.
Certes, on pourrait nettoyer le heap avant chaque appel avec
gc.collect()
, mais ce ne serait pas juste puisque nous n'aurons pas cette possibilité sur nombre de modèles concurrents. Il nous faut donc trouver autre chose.Précisons que l'absence du module gc et donc de
gc.collect()
ne signifie absolument pas que le heap ne sera jamais nettoyé. C'est juste que nous ne contrôlons pas le moment où il le sera.Et bien voici l'élément final du protocole de test que nous te proposons, avec une boucle répétant des appels
mem(0)
, ce qui devrait finir par déclencher des nettoyages du heap, et te signalant à chaque fois que la valeur retournée bat ainsi un nouveau record :- Code: Tout sélectionner
def testmem():
m1, m2 = 0, 0
while 1:
t1, t2 = mem(0)
if t1 > m1 or t2 > m2:
m1 = max(t1, m1)
m2 = max(t2, m2)
input(str((m1,m2)))
Sur les TI-83 Premium CE Edition Python et TI-84 Plus CE-T Python Edition, nous pouvons donc exploiter jusqu'à 17,601 Ko de heap.
C'est extrêmement faible, tes projets Python ne pourront pas aller bien loin !
Ce n'est pas la panacée mais c'est quand même sensiblement mieux, avec 19,496 Ko. Cela s'explique par l'absence de nombre de modules rajoutés dans le contexte de la TI-83 Premium CE Edition Python, dont les modules de tracé.
Si tu l'installes tu bénéficieras donc d'un espace heap disponible nettement amélioré, avec 22,158 Ko.
C'est donc au-delà de la capacité heap de 19,968 Ko trouvée plus haut pour le firmware officiel, mais c'est normal puisque l'on se rend compte que Lionel a en effet passé la capacité heap à 22,912 Ko.
Encore une fois, cette amélioration cache en réalité l'absence de nombre de modules.
Nous sommes certes moins à l'étroit que sur les modèles précédents, il y a de quoi commencer à aborder des projets un peu plus gourmands en ressources, mais pas aller très loin non plus.
- Graph 75+E
- Graph 35+E via une installation du système Graph 75+E
- Graph 35+E II
Sur les deux premiers nous nous envolons à pas moins de 257,026 Ko !
En effet selon le module gc, la capacité heap a ici été réglée à 258,048 Ko.
Nous n'obtenons alors qu'un espace heap libre de 31,163 Ko, ici donc sans aucun intérêt par rapport à l'application Python officielle.
Nous avons donc par défaut 1024,540 Ko de heap, soit 1,025 Mo !
KhiCAS intègre de plus son propre mode examen que tu peux activer depuis ses menus. Parfaitement compatible avec celui de Texas Instruments, ce mode examen a le gros avantage de laisser KhiCAS disponible !
De plus, l'installation de Micropython nécessite Ndless.
À ce jour Ndless n'est pas installable sur les TI-Nspire CX II et TI-Nspire CX munies des dernières mises à jour 5.3.2 ou 4.5.5, et il n'y a aucun moyen de revenir à une version précédente.
Dommage, pour le moment nous ne pourrons donc pas en tenir compte cette année.
En creusant un petit peu grâce au module gc ici disponible, nous découvrons que la capacité heap est de 2,049 Mo.
À ce jour Ndless n'est pas installable sur les TI-Nspire CX II et TI-Nspire CX munies des dernières mises à jour 5.3.2 ou 4.5.5, et il n'y a aucun moyen de revenir à une version précédente.
De plus, Micropython disparaîtra en mode examen.
Distinguons les HP Prime G1 avec 32 Mio de SDRAM, et HP Prime G2 avec 256 Mio de SDRAM, testées toutes deux juste après un reset :
- La HP Prime G1 offre 1020,293 Ko de heap, soit 1,020 Mo
- La HP Prime G2 offre 1023,804 Ko de heap, soit 1,024 Mo
Shift
Plot.
- Sur pas moins de 3 HP Prime G1, on arrive à régler très exactement jusqu'à 16127 Ko avant plantage.
- Sur HP Prime G2, on arrive à régler à peine davantage, jusqu'à 16384 Ko, un bug nous interdisant de régler des valeurs supérieures via cette boîte de dialogue.
Mais ne nous avouons pas encore vaincus. Les HP Prime permettent d'exécuter des scripts et fonctions Python depuis l'éditeur de programmes historique grâce à la fonction PYTHON(). Or, il se trouve que cette fonction permet de spécifier la capacité heap à allouer :
PYTHON({nom_python,taille_heap},...)
.Réalisons de quoi saisir et tester des capacités :
- Code: Tout sélectionner
#python heaptest_python
from gc import mem_alloc,mem_free
a,f=mem_alloc(),mem_free()
print("allocated heap: "+str(f+a))
print("free heap: "+str(f))
#end
Export heaptest_ppl(v)
Begin
PRINT("allocating heap: "+v);
PRINT("");
PYTHON({heaptest_python,v});
End;
La HP Prime G2 nous permet ainsi de spécifier jusqu'à 258 Mo et quelques, en pratique restons sur 258 Mo.
La capacité heap alors réellement reportée par le module gc est d'environ 252,1 Mo !
- 252,1 Mo : HP Prime G2
- 15,6 Mo : HP Prime G1
- 2,051 Mo : TI-Nspire CX II
- 1,033 Mo : Casio Graph 90+E
- 100,432 Ko : Casio Graph 35+E II
- 32,571 Ko : NumWorks
- 19,700 Ko : TI-82 Advanced Edition Python
- 17,601 Ko : TI-83 Premium CE Edition Python
- 252,1 Mo : HP Prime G2
- 15,6 Mo : HP Prime G1
- 2,051 Mo : TI-Nspire CX II
- 1,033 Mo : Casio Graph 90+E
- 1,025 Mo : TI-Nspire CX (Ndless + KhiCAS)
- 100,432 Ko : Casio Graph 35+E II
- 98,928 Ko : NumWorks (firmware Omega)
- 64,954 Ko : NumWorks N0110 (firmware Delta / Omega + appli KhiCAS)
- 32,571 Ko : NumWorks
- 25,235 Ko : NumWorks N0110 (firmware Delta)
- 19,700 Ko : TI-82 Advanced Edition Python
- 17,601 Ko : TI-83 Premium CE Edition Python
- 252,1 Mo : HP Prime G2
- 15,6 Mo : HP Prime G1
- 2,051 Mo : TI-Nspire CX II
- 1,033 Mo : Casio Graph 90+E / fx-CG50
- 100,432 Ko : Casio Graph 35+E II / fx-9750/9860GIII
- 32,571 Ko : NumWorks
- 19,496 Ko : TI-83 Premium CE + TI-Python
- 19,700 Ko : TI-82 Advanced Edition Python
- 17,601 Ko : TI-83 Premium CE Edition Python / TI-84 Plus CE Python Edition
- 252,1 Mo : HP Prime G2
- 15,6 Mo : HP Prime G1
- 2,051 Mo : TI-Nspire CX II
- 2,049 Mo : TI-Nspire (Ndless + MicroPython)
- 1,033 Mo : Casio Graph 90+E / fx-CG50
- 1,025 Mo : TI-Nspire CX / CX II (Ndless + KhiCAS CX / KhiCAS CX II)
- 257,026 Ko : Casio Graph 35/75+E / 35/75/95 / fx-9750/9860GII (SH4 - appli CasioPython)
- 100,432 Ko : Casio Graph 35+E II / fx-9750/9860GIII
- 98,928 Ko : NumWorks (firmware Omega)
- 64,954 Ko : NumWorks N0110 (firmware Omega + appli KhiCAS)
- 32,571 Ko : NumWorks
- 32,256 Ko : Casio Graph 35+E II / 35/75/85/95(SH3) / fx-9750/9860GIII / fx-9750/9860GII(SH3) / fx-9860G (appli CasioPython)
- 25,235 Ko : NumWorks N0110 (firmware Delta)
- 22,158 Ko : TI-83 Premium CE + TI-Python (firmware tiers)
- 19,496 Ko : TI-83 Premium CE + TI-Python
- 19,700 Ko : TI-82 Advanced Edition Python
- 17,601 Ko : TI-83 Premium CE Edition Python / TI-84 Plus CE Python Edition