Ouvrir un fichier par tranches ?

Dans le cas d'un programme de serveur en Micropython sur ESP 32 qui utilise quelques fichiers de librairies js (on parle de fichiers de quelques kb) : j'ai le résultat attendu. Par contre, j'ai un problème par moment de mémoire type "Memory Allocation failed for xxxxx bytes", notamment à la répétition du chargement du fichier ou si j'utilise une plus grosse librairie.

Je soupçonne fortement le fait que les fichiers ouverts viennent saturer la mémoire RAM. Une première option est évidemment d'utiliser que de petites librairies, ce qui est le cas en l'occurrence. Ou de mettre les libs à servir sur le raspberry pi par exemple qui fait aussi le broker MQTT du réseau local, etc. Mais j'aimerai ici me concentrer sur les possibilités d'optimiser en micropython le chargement du fichier pour ne pas saturer la RAM.

J'ai mis en entête ce qui est conseillé sur le site Micropython : https://docs.micropython.org/en/latest/library/micropython.html?highlight=micropython%20module#micropython.alloc_emergency_exception_buf

[code] import micropython micropython.alloc_emergency_exception_buf() [/code]

Je fais d'autre part un gc.collect() à intervalles régulier, lorsque la gestion d'une requête est terminée. Cela a bien amélioré les choses.

Je me demande si une bonne solution se serait de pouvoir lire le fichier sans le charger totalement en mémoire, ce qui est le cas par défaut avec un f.read().

J'ai lu ici : https://www.quora.com/How-do-I-load-large-files-without-using-a-lot-of-RAM-in-Python qu'on peut passer par l'iterateur plutôt que le fichier entier qui va lire ligne par ligne en faisant :

[code] f = open("file") for line in f: ... [/code]

Reste à savoir si cela va fonctionner en mode binaire f = open("file", "rb") : je vais faire quelques tests pour vérifier ce que çà donne avec mesure de la RAM résiduelle aux différentes étapes.

Il y a aussi ce post qui est intéressant autour du sujet mais pour le chargement de fichiers avec request : l'idée est de l'ouvrir par "tranches" à l'aide .iter_content(size) qui revient à un équivalent de ligne à ligne, en tout cas bloc à bloc, et en binaire. https://stackoverflow.com/questions/16694907/download-large-file-in-python-with-requests

L'idée est reprise ici pour défiler le fichier binaire par bloc mais aussi un fichier texte : https://stackoverflow.com/questions/4566498/what-is-the-idiomatic-way-to-iterate-over-a-binary-file-in-python

En fait le sujet est détaillé qu'il n'y paraît :

  • pour un fichier texte simple : on peut ouvrir ligne à ligne
  • mais un fichier texte qui est minifié cette façon de faire ne fonctionnera pas
  • on peut aussi envoyer par tranche pour un fichier binaire en faisant :
with open(path, 'rb') as file:
    while True:
        chunk = file.read(1024 * 64)
        if not chunk:
            break
        # handle the chunk

Se pose aussi la question de l'envoi de la réponse en "tranches" au client dans le cas d'un serveur.

En micropython, l'ouverture de fichier repose par défaut sur uio : https://docs.micropython.org/en/latest/library/uio.html

Existe aussi uasyncio qui implémente gestion asynchrone, mais çà ne concerne pas directement de sujet à priori.