J'ai eu un problème similaire en lisant un graphique à partir d'un fichier. Le traitement comprenait le calcul d'une matrice flottante de 200 000 x 200 000 (une ligne à la fois) qui ne tenait pas en mémoire. Essayer de libérer de la mémoire entre les calculs en utilisant gc.collect()
fixe l'aspect lié à la mémoire du problème, mais cela a entraîné des problèmes de performances: je ne sais pas pourquoi, mais même si la quantité de mémoire utilisée est restée constante, chaque nouvel appel a gc.collect()
pris plus de temps que le précédent. Donc, assez rapidement, la collecte des ordures a pris la plupart du temps de calcul.
Pour résoudre à la fois les problèmes de mémoire et de performances, je suis passé à l'utilisation d'une astuce multithreading que j'ai lue une fois quelque part (je suis désolé, je ne trouve plus le message correspondant). Avant, je lisais chaque ligne du fichier dans une grande for
boucle, le traitais et l'exécutais de gc.collect()
temps en temps pour libérer de l'espace mémoire. Maintenant, j'appelle une fonction qui lit et traite une partie du fichier dans un nouveau thread. Une fois le thread terminé, la mémoire est automatiquement libérée sans l'étrange problème de performances.
Pratiquement, cela fonctionne comme ceci:
from dask import delayed # this module wraps the multithreading
def f(storage, index, chunk_size): # the processing function
# read the chunk of size chunk_size starting at index in the file
# process it using data in storage if needed
# append data needed for further computations to storage
return storage
partial_result = delayed([]) # put into the delayed() the constructor for your data structure
# I personally use "delayed(nx.Graph())" since I am creating a networkx Graph
chunk_size = 100 # ideally you want this as big as possible while still enabling the computations to fit in memory
for index in range(0, len(file), chunk_size):
# we indicates to dask that we will want to apply f to the parameters partial_result, index, chunk_size
partial_result = delayed(f)(partial_result, index, chunk_size)
# no computations are done yet !
# dask will spawn a thread to run f(partial_result, index, chunk_size) once we call partial_result.compute()
# passing the previous "partial_result" variable in the parameters assures a chunk will only be processed after the previous one is done
# it also allows you to use the results of the processing of the previous chunks in the file if needed
# this launches all the computations
result = partial_result.compute()
# one thread is spawned for each "delayed" one at a time to compute its result
# dask then closes the tread, which solves the memory freeing issue
# the strange performance issue with gc.collect() is also avoided