[python] 파이썬에서 명시 적으로 메모리를 비울 수 있습니까?

삼각형을 나타내는 수백만 개의 객체를 만들기 위해 큰 입력 파일에서 작동하는 Python 프로그램을 작성했습니다. 알고리즘은 다음과 같습니다.

  1. 입력 파일을 읽습니다
  2. 파일을 처리하고 정점으로 표시되는 삼각형 목록을 만듭니다.
  3. 정점을 OFF 형식으로 출력합니다 : 정점 목록과 삼각형 목록. 삼각형은 정점 목록에 인덱스로 표시됩니다.

삼각형을 인쇄하기 전에 정점의 전체 목록을 인쇄 해야하는 OFF의 요구 사항은 출력을 파일에 쓰기 전에 삼각형 목록을 메모리에 보관해야한다는 것을 의미합니다. 한편 목록의 크기 때문에 메모리 오류가 발생합니다.

파이썬에게 더 이상 데이터가 필요하지 않다고 알려주는 가장 좋은 방법은 무엇입니까?



답변

Python Official Documentation 에 따르면 가비지 콜렉터가 강제로 참조되지 않은 메모리를 해제하도록 할 수 있습니다 gc.collect(). 예:

import gc
gc.collect()


답변

불행히도 (파이썬 버전과 릴리스에 따라) 일부 유형의 객체는 깔끔한 로컬 최적화이지만 “특정 유형의 객체에 대해서만 더 많은 메모리를”이어 마킹 “하여 메모리 조각화를 일으킬 수있는”무료 목록 “을 사용합니다. 따라서 “일반 기금”에는 이용할 수 없습니다.

메모리의 대량이지만 일시적으로 사용되는 DOES가 모든 자원을 시스템에 반환하도록 보장하는 유일하게 신뢰할 수있는 방법은 사용이 하위 프로세스에서 발생하도록하는 것인데, 이로 인해 메모리 부족한 작업이 종료됩니다. 이러한 조건에서 운영 체제는 작업을 수행하고 하위 프로세스가 고갈 된 모든 리소스를 기꺼이 재활용합니다. 다행스럽게도,이 multiprocessing모듈은 현대 버전의 파이썬에서 이런 종류의 작업을 (아주 고통 스러웠던) 너무 나쁘지 않게 만듭니다.

유스 케이스에서 하위 프로세스가 일부 결과를 누적하고 결과를 기본 프로세스에서 사용할 수 있도록하는 가장 좋은 방법은 반 임시 파일을 사용하는 것입니다. 닫히면 자동으로 사라지고 일반 파일은 모두 삭제하면 명시 적으로 삭제됩니다.


답변

del명령문은 사용 중일 수 있지만 IIRC 는 메모리를 해제한다고 보장하지 않습니다 . 문서는 여기에 있습니다 … 그리고 출시되지 않는 이유가 여기에있다 .

Linux 및 Unix 유형 시스템의 사람들이 파이썬 프로세스를 통해 작업을 수행하고 결과를 얻은 다음 죽이는 것을 들었습니다.

이 기사 에는 Python 가비지 수집기에 대한 메모가 있지만 메모리 제어 부족은 관리되는 메모리의 단점 이라고 생각 합니다.


답변

파이썬은 가비지 수집되므로 목록의 크기를 줄이면 메모리가 회수됩니다. “del”문을 사용하여 변수를 완전히 제거 할 수도 있습니다.

biglist = [blah,blah,blah]
#...
del biglist


답변

메모리를 명시 적으로 비울 수 없습니다. 객체에 대한 참조를 유지하지 않도록해야합니다. 그런 다음 가비지 수집되어 메모리를 비 웁니다.

큰 목록이 필요한 경우 일반적으로 생성기 / 반복기를 대신 사용하여 코드를 재구성해야합니다. 그렇게하면 메모리에 큰 목록이 필요하지 않습니다.

http://www.prasannatech.net/2009/07/introduction-python-generators.html


답변

( del객체는 객체에 대한 다른 참조가 없을 때 객체를 삭제 가능한 것으로 표시하므로 친구가 될 수 있습니다. 이제 CPython 인터프리터는 나중에 사용하기 위해이 메모리를 유지하므로 운영 체제에 “사용 가능한”메모리가 표시되지 않을 수 있습니다.)

데이터에 더 컴팩트 한 구조를 사용하여 처음에는 메모리 문제가 발생하지 않을 수 있습니다. 따라서 숫자 목록은 표준 array모듈 또는 타사 numpy모듈 에서 사용하는 형식보다 메모리 효율성이 훨씬 떨어 집니다. 정점을 NumPy 3xN 배열에, 삼각형을 N 요소 배열에 넣으면 메모리가 절약됩니다.


답변

파일에서 그래프를 읽는 데 비슷한 문제가있었습니다. 이 처리에는 메모리에 맞지 않는 200,000×200,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