Chargement de tableaux NumPy à partir du disque: comparaison de memmap () et Zarr / HDF5

Si votre tableau NumPy est trop grand pour tenir dans la RAM, vous pouvez le traiter en le divisant en fragments . Vous pouvez le faire en mode transparent ou explicitement en chargeant ces fragments à partir du disque un par un. Dans cette situation, vous pouvez recourir à deux classes d'outils:





  • La méthode NumPy memmap(), un mécanisme transparent qui vous permet de percevoir un fichier situé sur un disque comme s'il était entièrement en mémoire. 
  • Formats de stockage de données Zarr et HDF5 similaires les uns aux autres, qui permettent, si nécessaire, de charger à partir du disque et d'enregistrer des fragments compressés de la matrice sur disque.

Chacune de ces méthodes a ses propres forces et faiblesses. 

Le matériel, dont nous publions la traduction aujourd'hui, est consacré à l'analyse des caractéristiques de ces méthodes de travail avec les données, et à l'histoire des situations dans lesquelles elles peuvent être utiles. En particulier, une attention particulière sera accordée aux formats de données qui sont optimisés pour effectuer des calculs et qui ne sont pas nécessairement conçus pour transférer ces données à d'autres programmeurs.

Que se passe-t-il lors de la lecture de données à partir d'un disque ou de l'écriture de données sur un disque?


Lorsqu'un fichier est lu sur le disque pour la première fois, le système d'exploitation ne copie pas simplement les données dans la mémoire de processus. Tout d'abord, il copie ces données dans sa mémoire, en en stockant une copie dans le soi-disant «cache tampon».

Quelle est l'utilité ici?

Le fait est que le système d'exploitation stocke les données dans le cache au cas où vous auriez besoin de relire les mêmes données à partir du même fichier.


Si les données sont relues, elles entrent dans la mémoire du programme non pas à partir du disque, mais à partir de la RAM, ce qui est plus rapide de plusieurs ordres de grandeur.


Si la mémoire occupée par le cache est nécessaire pour autre chose, le cache sera automatiquement vidé.

Lorsque les données sont écrites sur le disque, elles se déplacent dans la direction opposée. Au début, ils sont écrits uniquement dans le cache tampon. Cela signifie que les opérations d'écriture sont généralement très rapides, car le programme n'a pas besoin de se concentrer sur un disque lent. Elle, pendant l'enregistrement, n'a besoin de travailler qu'avec de la RAM.


Par conséquent, les données sont vidées sur le disque à partir du cache.


Utilisation d'un tableau à l'aide de memmap ()


Dans notre cas, memmap()cela nous permet de percevoir un fichier sur disque comme s'il s'agissait d'un tableau stocké en mémoire. Le système d'exploitation, transparent pour le programme, effectue des opérations de lecture / écriture, accédant soit au cache tampon soit au disque dur, selon que les données demandées sont mises en cache ou non en mémoire. Un algorithme comme celui-ci est exécuté ici:

  • Les données sont-elles dans le cache? Si c'est le cas - génial - vous pouvez les contacter directement.
  • Les données sont-elles sur le disque? Leur accès sera plus lent, mais vous n'aurez pas à vous en soucier, ils seront chargés en mode transparent.

Comme avantage supplémentaire, memmap()on peut noter que dans la plupart des cas, le cache de tampon pour le fichier sera intégré dans la mémoire du programme. Cela signifie que le système n'a pas à conserver une copie supplémentaire des données dans la mémoire du programme en dehors de la mémoire tampon.


La méthode memmap()est intégrée à NumPy:

import numpy as np
array = np.memmap("mydata/myarray.arr", mode="r",
                  dtype=np.int16, shape=(1024, 1024))

Exécutez ce code et vous aurez à votre disposition un tableau dont le travail sera complètement transparent pour le programme, que le travail soit effectué avec le cache de tampon ou avec le disque dur.

Limitations de Memmap ()


Bien que dans certaines situations, elle memmap()puisse se montrer assez bien, cette méthode a également des limites:

  • Les données doivent être stockées dans le système de fichiers. Les données ne peuvent pas être téléchargées à partir du stockage binaire comme AWS S3.
  • , . , . , , , .
  • N- , , , . .

Expliquons le dernier point. Imaginez que nous ayons un tableau à deux dimensions contenant des entiers 32 bits (4 octets). 4096 octets sont lus par disque. Si vous lisez des données situées dans un fichier de manière séquentielle à partir d'un disque (par exemple, ces données sont dans les lignes d'un tableau), après chaque opération de lecture, nous aurons 1024 entiers. Mais si vous lisez des données dont l'emplacement dans le fichier ne correspond pas à leur emplacement dans le tableau (par exemple, les données situées dans les colonnes), chaque opération de lecture vous permettra d'obtenir seulement 1 numéro requis. En conséquence, il s'avère que pour obtenir la même quantité de données, vous devez effectuer mille fois plus d'opérations de lecture.

Zarr et HDF5


Afin de surmonter les limitations ci-dessus, vous pouvez utiliser des formats de stockage de données Zarr ou HDF5, qui sont très similaires:

  • Vous pouvez travailler avec des fichiers HDF5 en Python en utilisant pytables ou h5py . Ce format est plus ancien que Zarr et a plus de restrictions, mais son avantage est qu'il peut être utilisé dans des programmes écrits dans différentes langues.
  • Zarr est un format implémenté à l'aide du package Python du même nom. Il est beaucoup plus moderne et flexible que HDF5, mais vous ne pouvez l'utiliser (au moins pour l'instant) que dans l'environnement Python. Selon mes sentiments, dans la plupart des situations, s'il n'y a pas besoin de support multilingue pour HDF5, cela vaut la peine de choisir Zarr. Zarr, par exemple, a un meilleur support multithreading.

De plus, nous ne discuterons que de Zarr, mais si vous êtes intéressé par le format HDF5 et sa comparaison plus approfondie avec Zarr, vous pouvez regarder cette vidéo.

Utilisation de Zarr


Zarr vous permet de stocker des éléments de données et de les charger en mémoire sous forme de tableaux, et également - d'écrire ces éléments de données sous forme de tableaux.

Voici comment charger un tableau à l'aide de Zarr:

>>> import zarr, numpy as np
>>> z = zarr.open('example.zarr', mode='a',
...               shape=(1024, 1024),
...               chunks=(512,512), dtype=np.int16)
>>> type(z)
<class 'zarr.core.Array'>
>>> type(z[100:200])
<class 'numpy.ndarray'>

Veuillez noter que jusqu'à réception d'une tranche de l'objet, nous ne serons pas à notre disposition numpy.ndarray. Une entité zarr.core.arrayn'est que des métadonnées. Seules les données incluses dans la tranche sont chargées à partir du disque.

Pourquoi ai-je choisi Zarr?


  • Zarr contourne les limitations memmap()discutées ci-dessus:
  • Les fragments de données peuvent être stockés sur disque, dans le stockage AWS S3 ou dans un système de stockage qui offre la possibilité de travailler avec des enregistrements de format clé / valeur.
  • La taille et la structure du fragment de données sont déterminées par le programmeur. Par exemple, les données peuvent être organisées de manière à pouvoir lire efficacement des informations situées sur différents axes d'un réseau multidimensionnel. Cela est vrai pour HDF5.
  • Les fragments peuvent être compressés. La même chose peut être dite de HDF5.

Arrêtons-nous plus en détail sur les deux derniers points.

Dimensions des fragments


Supposons que nous travaillons avec un tableau de 30 000 x 3 000 éléments. Si vous devez lire ce tableau et vous déplacer le long de son axe Xet se déplacer le long de son axe Y, vous pouvez enregistrer des fragments contenant les données de ce tableau, comme indiqué ci-dessous (en pratique, vous aurez probablement besoin de plus de 9 fragments):


Désormais, les données situées à la fois sur l'axe Xet sur l'axe Ypeuvent être chargées efficacement. Selon le type de données nécessaires dans le programme, vous pouvez télécharger, par exemple, des fragments (1, 0), (1, 1), (1, 2) ou des fragments (0, 1), (1, 1), (2, 1).

Compression des données


Chaque fragment peut être compressé. Cela signifie que les données peuvent entrer dans le programme plus rapidement que le disque ne vous permet de lire les informations non compressées. Si les données sont compressées 3 fois, cela signifie qu'elles peuvent être téléchargées du disque 3 fois plus rapidement que les données non compressées, moins le temps nécessaire au processeur pour les décompresser.


Une fois les fragments téléchargés, ils peuvent être supprimés de la mémoire du programme.

Résumé: memmap () ou Zarr?


memmap()Quel est le meilleur à utiliser - ou Zarr?

Memmap()Cela semble intéressant dans de tels cas:

  • Il existe de nombreux processus qui lisent des parties du même fichier. Ces processus, grâce à l'application memmap(), pourront partager le même cache tampon. Cela signifie qu'une seule copie des données doit être conservée en mémoire, quel que soit le nombre de processus en cours d'exécution.
  • Le développeur n'a aucune envie de gérer manuellement la mémoire. Il prévoit de simplement s'appuyer sur les capacités du système d'exploitation, ce qui résoudra tous les problèmes de gestion de la mémoire automatiquement et de manière invisible pour le développeur.

Zarr est particulièrement utile dans les situations suivantes (dans certains d'entre eux, comme cela sera noté, le format HDF5 est également applicable):

  • Les données sont téléchargées à partir de sources distantes et non à partir du système de fichiers local.
  • Il est très probable que le goulot d'étranglement du système lira à partir du disque. La compression des données permettra une utilisation plus efficace des capacités matérielles. Cela vaut également pour HDF5.
  • Si vous avez besoin d'obtenir des tranches de tableaux multidimensionnels le long de différents axes, Zarr aide à optimiser ces opérations en sélectionnant la taille et la structure appropriées des fragments. Cela est vrai pour HDF5.

Je choisirais entre memmap()Zarr et, tout d'abord, j'essaierais d'utiliser Zarr - en raison de la flexibilité que ce package offre et du format de stockage de données qu'il implémente.

Chers lecteurs! Comment résolvez-vous le problème de travailler avec de grands tableaux NumPy?


All Articles