본문 바로가기
Big Data

tensorflow serving memory leak??

by 유주원 2024. 4. 2.

아래의 docker hub를 통해 tensorflow serving 이미지를 다운 받는다.

 

https://hub.docker.com/layers/tensorflow/serving/2.14.1-gpu/images/sha256-cdae1c60c07d1ffc94ebc086e0f1a4d7f1ecc52f3fec558f1f7b506dfd4edce2?context=explore

 

Docker

 

hub.docker.com

 

$> docker pull tensorflow/serving:2.14.1-gpu

 

model은 dockerhub git에 있는 예제 프로그램 대로 실행해본다.

1. 우선은 git 받기

 

$> git clone https://github.com/tensorflow/serving

 

2. 환경 변수 정의하기

$> TESTDATA="$(pwd)/serving/tensorflow_serving/servables/tensorflow/testdata"

 

3. docker 띄우기

$> docker run -t --rm -p 8501:8501 -v "$TESTDATA/saved_model_half_plus_two_gpu:/models/half_plus_two"  -e MODEL_NAME=half_plus_two tensorflow/serving:2.14.1-gpu &

 

4. inference test 하기

$> curl -d '{"instances": [1.0, 2.0, 5.0]}' -X POST http://localhost:8501/v1/models/half_plus_two:predict

 

이제 해당 컨테이너 내부로 들어가서 메모리를 확인해보자.

$> docker exec -it [컨테이너 이름] bash
$> while true; do pmap -x [프로세스 번호] | tail -1; sleep 1; done

 

 

rss 값이 해당 tensorflow server api를 호출할 때마다 증가하지만, 호출이 끝나도 감소하지 않는 것을 확인할 수 있다.

그럼 이게 메모리 릭인가???

rss에 대해 좀 더 자세히 살펴 보자.

rss는 Resident Set Size의 약자로 해당 프로세스가 실제 점유하고 있는 RAM의 크기를 의미한다고 한다. 하지만 shared memory의 크기 또한 각각 상정하기 때문에 생각한 것 보다 메모리가 더 크게 잡힐 수가 있다. 여기서 shared memory는 shared library라고 생각하면 쉽다. so 파일이나 lib 파일 등을 의미한다. A process와 B process 모두 동일한 so 파일을 사용하고 있어도, rss에서는 각각 잡히기 때문에 생각한 것보다 메모리가 크게 잡힐 수가 있다.

 

그런데  tensorflow serving에서는 왜 호출이 끝났는데도 이 rss 메모리를 계속 들고 있는 것일까? 

아 골치 아프다...

 

아래의 구글 discussion 발견..

https://groups.google.com/g/comp.unix.solaris/c/Ed_hGn4-Eto

 

RSS keeps increasing...Memory leaks??

>Subject: Re: RSS keeps increasing...Memory leaks?? >From: Rich Teer rich...@rite-group.com >Date: 11/25/04 7:37 AM Hawaiian Standard Time >Message-id: <Pine.SOL.4.58.0411250934221.24690@zaphod> > >On Thu, 25 Nov 2004, qazmlp wrote: > >> RSS(resident set s

groups.google.com

 

희망적인 내용은... 메모리 누수가 있는 프로그램의 경우 rss가 지속적으로 증가하는 것은 맞다. 하지만 정상적인 프로그램에서도 rss가 증가 할 수가 있다. 메모리 free를 할 때 곧바로 커널에 반환되지 않고, 다시 재사용 될 수도 있다.(realloc) 

 

하지만.. 호출이 중단된 시점에도 free하지 않고 계속 들고 있단 건 좀 의심해볼만한 것 같다..

 

계속 찾아보자...

 

테스트 1. tensorflow serving 이미지 내에 tcmalloc 설치하기

https://dhdroid.github.io/tensorflow/2021/10/11/detecting-tensorflow-memory-leak.html

 

TensorFlow 2.x 사용시 발생하는 memory leak 해결

TensorFlow 2.x에서 custom training loop을 작성하여 train을 진행하다 보면, epoch이나 step이 진행됨에 따라 memory usage가 조금씩 증가하는 경우가 종종 발생합니다. 그 중 제가 마주했던 2가지 원인들을 간략

dhdroid.github.io

 

기존 malloc를 이용하여 data iteration만 진행해도 memory leak이 발생할 수 있다고 한다. 그래서 위의 블로그에서는 google에서 개발한 tcmalloc을 추천하고 있다.

 

해당 이미지 내로 들어간 다음 아래와 같이 실행하자.

$> docker exec -it [컨테이너 이름] bash
$> apt-get install libtcmalloc-minimal4
$> dpkg -L libtcmalloc-minimal4  # libtcmalloc_minimal.so.4 위치 확인
$> LD_PRELOAD=[libtcmalloc_minimal.so.4 경로]

 

이 후 tensorflow serving은 위에 설명한 것처럼 docker 컨테이너와 모델을 띄워서 실행 시키자.

그리고 컨테이너 내부로 들어가서 메모리를 확인해 보자.

 

 

rss가 지속적으로 올라가다가 어느 순간부터 수렴하는 걸 확인할 수가 있다. 기존 malloc은 호출할 때마다 rss 메모리가 올라갔는데 tcmalloc의 경우는 일정 수준까지 올라가다가 수렴을 한다. 하지만 호출이 중단된 시점에서도 해당 메모리를 그대로 들고 있다.

음... 이건 메모리 릭이 아닌가... 점점 혼란이 온다.

 

메모리 릭인지 확인해 보기 위해 container에 메모리 제한을 건 후에 호출을 해보기로 했다. 우선 300m로 컨테이너 메모리 제한을 걸자.

 

$> docker run -t --rm -p 8501:8501 -m 300m -v "$TESTDATA/saved_model_half_plus_two_gpu:/models/half_plus_two"  -e MODEL_NAME=half_plus_two tensorflow/serving:2.14.1-gpu &

 

이 후에 해당 tfs를 각각 5 thread -> 10 thread -> 5 thread -> 15 thread -> 20 thread로 호출을 진행해 본다.

 

tcmalloc을 적용하지 않은 tfs에서는 10 thread 호출 시 컨테이너가 kill 됐다. 아무래도 tcmalloc이 일반 malloc보다 더 효율적으로 메모리를 사용하는 느낌이었다.

 

tcmalloc의 경우 5 thread -> 10 thread로 호출량을 늘릴수록 메모리 점유도 늘어났다.

 

아... 진짜 메모리 릭인가... 이번에는 5 thread로 줄여서 호출해 봤다. 어라?? 점유하고 있던 메모리에서 더 늘지 않았다.

이번에는 15, 20 thread로 점차 호출량을 늘려봤다. 메모리가 계속 해서 올라가다가 300m 언저리 부근부터 점유했다 해제했다를 반복했다. 아무래도 tfs에서 가지고 있는 메모리를 재사용하고 있는 느낌이었다. 만약에 메모리 누수라면 계속 상승을 쳐서 컨테이너가 죽었어야 했는데, 메모리 릭 같지는 않아보였다.

해당 실험 결과로 일반 malloc 보다 tcmalloc이 성능상 훨씬 좋다는 걸 확인하게 되었다. 또한 tfs의 경우 메모리를 점유하고 있는데.. 무슨 이유에선지 해제는 하지 않지만 메모리 릭으로 인해 점유되고 있는 느낌은 아니었다.