web-dev-qa-db-fra.com

Options d'enregistrement et de chargement les plus rapides pour un tableau numpy

J'ai un script qui génère des numpyarrays bidimensionnels avec dtype=float et forme de l'ordre de (1e3, 1e6). En ce moment j'utilise np.save et np.load pour effectuer IO opérations avec les tableaux. Cependant, ces fonctions prennent plusieurs secondes pour chaque tableau. Existe-t-il des méthodes plus rapides pour enregistrer et charger les tableaux entiers (c'est-à-dire sans faire d'hypothèses sur leur contenu) et les réduire)? Je suis prêt à convertir les array en un autre type avant de les sauvegarder tant que les données sont conservées exactement.

33
dbliss

Pour les très grandes baies, j'ai entendu parler de plusieurs solutions, et elles ont surtout été paresseuses sur les E/S:

  • NumPy.memmap , mappe les grands tableaux en forme binaire
    • Avantages :
      • Aucune dépendance autre que Numpy
      • Remplacement transparent de ndarray (Toute classe acceptant ndarray accepte memmap)
    • Les inconvénients :
      • Des morceaux de votre baie sont limités à 2,5 G
      • Toujours limité par le débit Numpy
  • Utilisez Python pour HDF5, un format de fichier prêt pour les bigdata, comme PyTables ou h5py

    • Avantages :
      • Le format prend en charge la compression, l'indexation et d'autres fonctionnalités super Nice
      • Apparemment, le format de fichier PetaByte ultime
    • Les inconvénients :
      • Courbe d'apprentissage d'avoir un format hiérarchique?
      • Vous devez définir quels sont vos besoins de performance (voir plus loin)
  • décapage de Python système (hors course, mentionné pour la Pythonicité plutôt que pour la vitesse)

    • Avantages:
      • C'est Pythonic! (haha)
      • Prend en charge toutes sortes d'objets
    • Les inconvénients:
      • Probablement plus lent que les autres (car il vise tous les objets et non les tableaux)

Numpy.memmap

À partir des documents de NumPy.memmap :

Créez une carte mémoire vers un tableau stocké dans un fichier binaire sur le disque.

Les fichiers mappés en mémoire sont utilisés pour accéder à de petits segments de fichiers volumineux sur le disque, sans lire l'intégralité du fichier en mémoire

L'objet memmap peut être utilisé partout où un ndarray est accepté. Étant donné n'importe quel memmap fp, isinstance(fp, numpy.ndarray) renvoie True.


Matrices HDF5

Depuis le doc h5py

Vous permet de stocker d'énormes quantités de données numériques et de manipuler facilement ces données à partir de NumPy. Par exemple, vous pouvez découper en ensembles de données de plusieurs téraoctets stockés sur le disque, comme s'il s'agissait de véritables tableaux NumPy. Des milliers d'ensembles de données peuvent être stockés dans un seul fichier, classés et étiquetés comme vous le souhaitez.

Le format prend en charge la compression des données de différentes manières (plus de bits chargés pour la même lecture d'E/S), mais cela signifie que les données deviennent moins faciles à interroger individuellement, mais dans votre cas (purement chargement/vidage de tableaux), cela pourrait être efficace

36
Jiby

Voici une comparaison avec PyTables.

Je ne peux pas me lever (int(1e3), int(1e6) en raison de restrictions de mémoire. Par conséquent, j'ai utilisé un tableau plus petit:

data = np.random.random((int(1e3), int(1e5)))

NumPy save:

%timeit np.save('array.npy', data)
1 loops, best of 3: 4.26 s per loop

NumPy load:

%timeit data2 = np.load('array.npy')
1 loops, best of 3: 3.43 s per loop

Écriture de PyTables:

%%timeit
with tables.open_file('array.tbl', 'w') as h5_file:
    h5_file.create_array('/', 'data', data)

1 loops, best of 3: 4.16 s per loop

Lecture de PyTables:

 %%timeit
 with tables.open_file('array.tbl', 'r') as h5_file:
      data2 = h5_file.root.data.read()

 1 loops, best of 3: 3.51 s per loop

Les chiffres sont très similaires. Donc pas de vrai gain avec PyTables ici. Mais nous sommes assez proches du taux d'écriture et de lecture maximum de mon SSD.

L'écriture:

Maximum write speed: 241.6 MB/s
PyTables write speed: 183.4 MB/s

En train de lire:

Maximum read speed: 250.2
PyTables read speed: 217.4

La compression n'aide pas vraiment en raison du caractère aléatoire des données:

%%timeit
FILTERS = tables.Filters(complib='blosc', complevel=5)
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file:
    h5_file.create_carray('/', 'data', obj=data)
1 loops, best of 3: 4.08 s per loop

La lecture des données compressées devient un peu plus lente:

%%timeit
with tables.open_file('array.tbl', 'r') as h5_file:
    data2 = h5_file.root.data.read()

1 loops, best of 3: 4.01 s per loop

Ceci est différent pour les données régulières:

 reg_data = np.ones((int(1e3), int(1e5)))

L'écriture est nettement plus rapide:

%%timeit
FILTERS = tables.Filters(complib='blosc', complevel=5)
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file:
    h5_file.create_carray('/', 'reg_data', obj=reg_data)

1 boucles, meilleur de 3: 849 ms par boucle

Il en va de même pour la lecture:

%%timeit
with tables.open_file('array.tbl', 'r') as h5_file:
    reg_data2 = h5_file.root.reg_data.read()

1 loops, best of 3: 1.7 s per loop

Conclusion: Plus vos données sont régulières, plus vite elles devraient utiliser PyTables.

21
Mike Müller

D'après mon expérience, np.save () & np.load () est la solution la plus rapide pour transférer des données entre le disque dur et la mémoire jusqu'à présent. J'ai fortement compté mes données de chargement sur la base de données et le système HDFS avant de réaliser cette conclusion. Mes tests montrent que: La bande passante de chargement des données de la base de données (du disque dur à la mémoire) pourrait être d'environ 50 Mbps (octets/seconde), mais la bande passante np.load () est presque la même que la bande passante maximale de mon disque dur: 2 Go/s (octets/Seconde). Les deux environnements de test utilisent la structure de données la plus simple.

Et je ne pense pas que ce soit un problème d'utiliser plusieurs secondes pour charger un tableau de forme: (1e3, 1e6). Par exemple. La forme de votre tableau est (1000, 1000000), son type de données est float128, puis la taille de données pure est (128/8) * 1000 * 1,000,000 = 16,000,000,000 = 16GBytes et si cela prend 4 secondes, votre bande passante de chargement de données est alors de 16GBytes/4 secondes = 4 Go/s. La bande passante maximale SATA3 est de 600 Mo/s = 0,6 Go/s, votre bande passante de chargement de données est déjà 6 fois supérieure, vos performances de chargement de données pourraient presque rivaliser avec bande passante maximale de DDR , que voulez-vous d'autre?

Donc ma conclusion finale est:

N'utilisez pas Pickle de python, n'utilisez aucune base de données, n'utilisez aucun système de Big Data pour stocker vos données sur le disque dur, si vous pouviez utiliser np.save () et np .charge(). Jusqu'à présent, ces deux fonctions sont la solution la plus rapide pour transférer des données entre le disque dur et la mémoire.

J'ai également testé le HDF5 , et j'ai trouvé qu'il était plus lent que np.load () et np.save (), alors utilisez np.save () & np.load () si vous avez suffisamment de mémoire DDR dans votre platfrom.

6
Clock ZHONG