파일에서 그래프를 읽는 데 비슷한 문제가있었습니다. 이 처리에는 메모리에 맞지 않는 200,000x200,000 부동 행렬 (한 번에 한 줄)의 계산이 포함되었습니다. gc.collect()
문제의 메모리 관련 측면을 수정하여 계산간에 메모리를 확보하려고 시도 했지만 성능 문제가 발생했습니다. 왜 사용 된 메모리의 양이 일정하게 유지되었지만 새 호출마다 gc.collect()
시간이 더 걸렸습니다. 이전 것. 따라서 가비지 수집이 계산 시간의 대부분을 차지했습니다.
메모리와 성능 문제를 해결하기 위해 한 번 읽은 멀티 스레딩 트릭을 사용하기로 전환했습니다 (죄송합니다, 더 이상 관련 게시물을 찾을 수 없습니다). 파일의 각 줄을 큰 for
루프 로 읽고 처리하고 gc.collect()
메모리 공간을 확보하기 위해 가끔씩 실행 하기 전에 . 이제 새 스레드에서 파일 청크를 읽고 처리하는 함수를 호출합니다. 스레드가 종료되면 이상한 성능 문제없이 메모리가 자동으로 해제됩니다.
실제로 다음과 같이 작동합니다.
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