제퍼넷 로고

Python에서 메모리 문제를 해결하는 방법

시간

Python에서 메모리 문제를 해결하는 방법

메모리 문제는 Python에서 진단하고 수정하기가 어렵습니다. 이 게시물은 인기있는 오픈 소스 Python 패키지를 사용하여 메모리 누수를 정확히 찾아 내고 수정하는 방법에 대한 단계별 프로세스를 거칩니다.


By 프레디 볼튼, Alteryx 소프트웨어 엔지니어

Python에서 메모리 문제를 해결하는 방법

응용 프로그램에 메모리가 부족하다는 사실을 알아내는 것은 개발자가 가질 수있는 최악의 현실 중 하나입니다. 메모리 문제는 일반적으로 진단하고 수정하기 어렵지만 Python에서는 더 어렵다고 생각합니다. Python의 자동 가비지 수집을 사용하면 언어를 쉽게 익히고 사용할 수 있지만 예상대로 작동하지 않을 때 개발자가 문제를 식별하고 해결하는 방법을 잃을 수 있습니다. .

이 블로그 게시물에서는 메모리 문제를 진단하고 수정 한 방법을 보여 드리겠습니다. 평가ML, Alteryx Innovation Labs에서 개발 한 오픈 소스 AutoML 라이브러리입니다. 메모리 문제를 해결하는 마법의 방법은 없지만 개발자, 특히 Python 개발자가 향후 이러한 종류의 문제가 발생할 때 활용할 수있는 도구와 모범 사례에 대해 배울 수 있기를 바랍니다.

이 블로그 게시물을 읽은 후 다음 사항을 확인해야합니다.

  1. 프로그램에서 메모리 문제를 찾아 수정하는 것이 중요한 이유,
  2. 순환 참조 란 무엇이며 Python에서 메모리 누수를 일으킬 수있는 이유
  3. Python의 메모리 프로파일 링 도구에 대한 지식과 메모리 문제의 원인을 식별하기 위해 취할 수있는 몇 가지 단계.

무대 설정

 
EvalML 팀은 성능 회귀를 포착하기 위해 새로운 버전의 패키지를 출시하기 전에 일련의 성능 테스트를 실행합니다. 이러한 성능 테스트에는 다양한 데이터 세트에서 AutoML 알고리즘을 실행하고, 알고리즘이 달성 한 점수와 런타임을 측정하고, 이러한 측정 항목을 이전에 출시 된 버전과 비교하는 것이 포함됩니다.

어느 날 테스트를 실행하다가 갑자기 애플리케이션이 다운되었습니다. 어떻게 된 거예요?

0 단계 – 메모리 란 무엇이며 누수는 무엇입니까?

 
모든 프로그래밍 언어의 가장 중요한 기능 중 하나는 컴퓨터 메모리에 정보를 저장하는 기능입니다. 프로그램이 새 변수를 만들 때마다 해당 변수의 내용을 저장하는 데 사용할 메모리를 할당합니다.

커널은 프로그램이 컴퓨터의 CPU, 메모리, 디스크 저장소 등에 액세스 할 수있는 인터페이스를 정의합니다. 모든 프로그래밍 언어는 실행중인 프로그램에서 사용할 메모리 청크를 할당 및 해제하도록 커널에 요청하는 방법을 제공합니다.

메모리 누수는 프로그램이 사용할 메모리 청크를 따로 남겨 두도록 커널에 요청하지만 버그 나 충돌로 인해 해당 메모리 사용이 끝났을 때 프로그램이 커널에 알리지 않을 때 발생합니다. 이 경우 커널은 잊혀진 메모리 청크가 실행중인 프로그램에서 계속 사용되고 있다고 계속 생각하며 다른 프로그램은 해당 메모리 청크에 액세스 할 수 없습니다.

프로그램을 실행하는 동안 동일한 누출이 반복적으로 발생하면 잊혀진 메모리의 총 크기가 너무 커져서 컴퓨터 메모리의 많은 부분을 차지할 수 있습니다! 이 상황에서 프로그램이 더 많은 메모리를 요청하려고하면 커널에서 "메모리 부족"오류가 발생하고 프로그램 실행이 중지됩니다. 즉, "충돌"이 발생합니다.

따라서 작성하는 프로그램에서 메모리 누수를 찾아 수정하는 것이 중요합니다. 그렇지 않으면 프로그램이 결국 메모리 부족과 충돌을 일으키거나 다른 프로그램이 충돌 할 수 있기 때문입니다.

1 단계 : 메모리 문제인지 확인

 
응용 프로그램은 여러 가지 이유로 충돌 할 수 있습니다. 코드를 실행하는 서버가 충돌하거나 코드 자체에 논리적 오류가있을 수 있으므로 당면한 문제가 메모리 문제라는 것을 확인하는 것이 중요합니다.

EvalML 성능 테스트는 엄청나게 조용한 방식으로 충돌했습니다. 갑자기 서버가 진행률 로깅을 중지하고 작업이 조용히 완료되었습니다. 서버 로그에는 코딩 오류로 인한 스택 추적이 표시되므로 사용 가능한 모든 메모리를 사용하는 작업으로 인해이 조용한 충돌이 발생했습니다.

성능 테스트를 다시 실행했지만 이번에는 Python의 메모리 프로파일 러 시간에 따른 메모리 사용량의 플롯을 얻을 수 있습니다. 테스트가 다시 충돌했고 메모리 플롯을 보았을 때 다음을 보았습니다.

차트, 라인 차트 설명 자동 생성성능 테스트의 메모리 프로필

 

우리의 메모리 사용량은 시간이 지남에 따라 안정적으로 유지되지만 8GB에 도달합니다! 응용 프로그램 서버에 8 기가 바이트의 RAM이 있다는 것을 알고 있으므로이 프로필은 메모리가 부족하다는 것을 확인합니다. 또한 메모리가 안정되면 약 4GB의 메모리를 사용하지만 이전 버전의 EvalML은 약 2GB의 메모리를 사용했습니다. 따라서 어떤 이유로이 현재 버전은 평소보다 약 XNUMX 배 많은 메모리를 사용하고 있습니다.

이제 그 이유를 알아 내야했습니다.

2 단계 : 최소한의 예제로 메모리 문제를 로컬로 재현

 
기억 문제의 원인을 정확히 찾아내는 데는 많은 실험과 반복이 필요합니다. 대답은 일반적으로 명확하지 않기 때문입니다. 그렇다면 아마도 코드에 작성하지 않았을 것입니다! 따라서 가능한 적은 코드로 문제를 재현하는 것이 중요하다고 생각합니다. 이 최소한의 예제를 통해 진행 중인지 확인하기 위해 코드를 수정하는 동안 프로파일 러에서 빠르게 실행할 수 있습니다.

필자의 경우 경험을 통해 애플리케이션이 큰 스파이크를 보았을 때 1.5 만 행의 택시 데이터 세트를 실행한다는 것을 알고있었습니다. 나는 우리의 응용 프로그램을 부품 이 데이터 세트를 실행합니다. 위에서 설명한 것과 비슷한 급증을 보았지만 이번에는 메모리 사용량이 10GB에 도달했습니다!

이것을 본 후, 나는 더 깊이 들어가기에 충분한 최소한의 예제가 있다는 것을 알았습니다.

차트, 분산 형 차트 설명 자동 생성택시 데이터 세트에 대한 로컬 재생산 기의 메모리 공간

 

3 단계 : 가장 많은 메모리를 할당하는 코드 줄 찾기

 
문제를 가능한 한 작은 코드 덩어리로 분리하면 프로그램이 가장 많은 메모리를 할당하는 위치를 확인할 수 있습니다. 이것은 코드를 리팩토링하고 문제를 해결하는 데 필요한 총이 될 수 있습니다.

내가 생각하기에 파일 프로파일 러 이를 수행하는 훌륭한 Python 도구입니다. 최대 메모리 사용량 시점에서 애플리케이션의 각 코드 줄에 대한 메모리 할당을 표시합니다. 이것은 내 로컬 예제의 출력입니다.

텍스트 설명 자동 생성fil-profile의 출력

 

filprofiler는 메모리 할당에 따라 애플리케이션의 코드 줄 (및 종속성 코드)의 순위를 매 깁니다. 선이 길고 붉을수록 더 많은 메모리가 할당됩니다.

가장 많은 메모리를 할당하는 줄은 pandas 데이터 프레임 (pandas / core / algorithms.py 및 pandas / core / internal / managers.py)을 만들고 4GB의 데이터에 달합니다! 여기서 filprofiler의 출력을 잘라 냈지만 pandas 데이터 프레임을 생성하는 EvalML의 코드로 pandas 코드를 추적 할 수 있습니다.

이것을 보는 것은 약간 당혹 스러웠습니다. 예, EvalML은 pandas 데이터 프레임을 생성하지만 이러한 데이터 프레임은 AutoML 알고리즘 전체에서 수명이 짧으며 더 이상 사용되지 않는 즉시 할당을 취소해야합니다. 이것이 사실이 아니고 이러한 데이터 프레임은 EvalML이 충분히 수행 된 시간 동안 메모리에 남아 있었기 때문에 최신 버전에서 메모리 누출.

4 단계 : 새는 물체 식별

 
Python의 맥락에서 누수 객체는 사용이 완료된 후 Python의 가비지 수집기에 의해 할당 해제되지 않는 객체입니다. 파이썬이 사용하기 때문에 참조 계산 기본 가비지 수집 알고리즘 중 하나 인 이러한 누수 개체는 일반적으로 개체에 대한 참조를 예상보다 오래 보유하고 있기 때문에 발생합니다.

이러한 종류의 객체는 찾기가 까다 롭지 만 검색을 다루기 쉽게 만드는 데 활용할 수있는 몇 가지 Python 도구가 있습니다. 첫 번째 도구는 gc.DEBUG_SAVEALL 가비지 수집기의 플래그입니다. 이 플래그를 설정하면 가비지 수집기는 연결할 수없는 개체를 gc.garbage 목록에 저장합니다. 이렇게하면 해당 개체를 더 자세히 조사 할 수 있습니다.

두 번째 도구는 객체 그래프 도서관. 객체가 gc.garbage 목록에 있으면이 목록을 pandas 데이터 프레임으로 필터링하고 objgraph를 사용하여 다른 객체가 이러한 데이터 프레임을 참조하고 메모리에 유지하는지 확인할 수 있습니다. 이 O'Reilly를 읽고이 접근 방식에 대한 아이디어를 얻었습니다. 블로그 게시물.

다음은 이러한 데이터 프레임 중 하나를 시각화 할 때 본 개체 그래프의 하위 집합입니다.

다이어그램 설명 자동 생성
Pandas 데이터 프레임에서 사용하는 메모리 그래프로 메모리 누수를 초래하는 순환 참조를 보여줍니다.

 

이건 내가 찾던 담배 총이야! 데이터 프레임은 PandasTableAccessor라는 것을 통해 자신에 대한 참조를 만듭니다. 순환 참조, 그래서 이것은 파이썬의 가비지 수집기가 실행되고 그것을 해제 할 수있을 때까지 객체를 메모리에 유지합니다. (dict, PandasTableAccessor, dict, _dataframe을 통해주기를 추적 할 수 있습니다.) EvalML에서는 가비지 수집기가 이러한 데이터 프레임을 메모리에 너무 오래 보관하여 메모리가 부족했기 때문에 문제가되었습니다.

PandasTableAccessor를 추적 할 수있었습니다. 목조 부분 도서관에 가져와 발행물 관리자까지. 새 릴리스에서 수정하고 관련 문서를 제출할 수있었습니다. 발행물 오픈 소스 생태계에서 가능한 협업의 좋은 예인 Pandas 저장소에

Woodwork 업데이트가 출시 된 후 동일한 데이터 프레임의 개체 그래프를 시각화하고주기가 사라졌습니다!

다이어그램 설명 자동 생성목공 업그레이드 후 pandas 데이터 프레임의 개체 그래프. 더 이상주기가 없습니다!

 

5 단계 : 수정 사항이 작동하는지 확인

 
EvalML에서 Woodwork 버전을 업그레이드 한 후 애플리케이션의 메모리 사용량을 측정했습니다. 이제 메모리 사용량이 예전의 절반도되지 않는다고보고하게되어 기쁩니다!

자동 생성 된 차트 설명수정 후 성능 테스트 메모리

 

닫는 생각

 
이 게시물의 시작 부분에서 말했듯이 메모리 문제를 해결하기위한 마법의 방법은 없지만이 사례 연구는 향후 이러한 상황에 직면 할 때 활용할 수있는 일반적인 프레임 워크와 도구 세트를 제공합니다. memory-profiler와 filprofiler가 Python에서 메모리 누수를 디버깅하는 데 유용한 도구임을 발견했습니다.

또한 Python의 순환 참조가 애플리케이션의 메모리 공간을 늘릴 수 있음을 강조하고 싶습니다. 가비지 수집기는 결국 메모리를 해제하지만이 경우에서 보았 듯이 너무 늦기 전까지는 그렇지 않을 수 있습니다!

순환 참조는 Python에서 의도하지 않게 도입하기가 놀랍도록 쉽습니다. 나는 찾을 수 있었다 의도하지 않은 것 EvalML에서 scikit 최적화및 Scipy. 눈을 떼지 마시고 야생에서 순환 참조를 본다면 실제로 필요한지 대화를 시작하십시오!

 
바이오 : 프레디 볼튼 Alteryx의 소프트웨어 엔지니어입니다. 그는 오픈 소스 프로젝트 작업을 즐기고 보스턴 곳곳에서 자전거를 타는 것을 즐깁니다.

실물. 허가를 받아 다시 게시했습니다.

관련 :

코인 스마트. 유로파 최고의 비트 코인-보르 스
출처 : https://www.kdnuggets.com/2021/06/troubleshoot-memory-problems-python.html

spot_img

최신 인텔리전스

spot_img

우리와 함께 채팅

안녕하세요! 어떻게 도와 드릴까요?