서버를 며칠 켜둔 채 쓰다 보면 어느 순간 생성이 굼떠집니다. 재시작하면 돌아오고요. 원인을 찾는 데 실험을 다섯 번 했고, 그 사이에 제가 앞서 공개한 원인이 두 번 뒤집혔습니다. 과정을 그대로 남깁니다. 틀린 쪽도 같이요.

결론

  • 큰 작업을 한 번 돌리면 그 뒤 같은 워크로드가 2.7~3.4배 느려집니다.
  • 모델을 이것저것 오가는 것과는 무관합니다. 체크포인트 3종을 순환해도 아무 일도 안 일어납니다.
  • 영상 때문도 아닙니다. 이미지만으로 2048×2048 한 장을 뽑아도 똑같이 생깁니다.
  • 진짜 원인은 PYTORCH_HIP_ALLOC_CONF=expandable_segments:True였습니다. 끄면 1.06배, 그러니까 사라집니다.
  • --reserve-vram을 늘리면 나아질 것 같은데 반대로 악화됩니다. 2.73배가 3.6배가 됩니다.

환경

Radeon AI PRO R9700(gfx1201, VRAM 31.86GiB), ROCm 7.2.4, torch 2.13.0+rocm7.2, ComfyUI 0.28.0, Proxmox VM passthrough(16코어/94GB), Ubuntu 24.04. 실행 옵션은 --use-pytorch-cross-attention --reserve-vram 1 --enable-assets, 환경변수로 PYTORCH_HIP_ALLOC_CONF=expandable_segments:True.

측정 워크로드는 전부 같습니다. SDXL base 1.0, 1024×1024, euler/normal, cfg 8.0, 배치 1, 20스텝. 회차마다 첫 실행은 모델 로드가 섞이므로 평균에서 뺐습니다.

실험 1 — 현상은 실재한다

며칠 굴린 끝에 VRAM이 꽉 찬 상태가 나왔습니다. 만들고 싶다고 만들어지는 조건이 아니라 그대로 재고, 재시작한 뒤 같은 걸 한 번 더 쟀습니다.

항목포화 상태재시작 직후배수
스텝당745.7 ms (1.34 it/s)214.3 ms (4.67 it/s)3.48배
20스텝 전체22.67 s5.66 s4.01배
40스텝 전체37.59 s9.95 s3.78배
고정 오버헤드7.76 s1.37 s5.65배
VRAM 피크30.25 GiB14.84 GiB 

스텝당 비용은 20스텝·40스텝을 각 3회 돌려 (40스텝 평균 - 20스텝 평균) / 20으로 냈습니다. 고정 비용이 상쇄돼 샘플링 속도만 남습니다.

오버헤드가 5.7배로 샘플링(3.5배)보다 더 크게 뛰는 게 눈에 띕니다. 실행할 때마다 가중치를 내렸다 올린다는 뜻이라 짧은 워크플로우일수록 손해가 큽니다. 재시작 직후 값이 예전에 올린 기준선(223밀리초, 4.49 it/s)과 거의 같아서, 포화 쪽 숫자도 믿을 만합니다.

실험 2 — 모델을 오가는 게 원인일까 (아니었다)

제가 처음 적었던 설명은 "큰 모델을 오가며 며칠 쓰면 잔여물이 쌓인다"였습니다. 확인해봤습니다. 깨끗한 상태에서 기준을 재고, 6.94GB짜리 체크포인트 3종을 차례로 올린 다음, 같은 워크로드를 다시 쟀습니다.

단계시간VRAM
before웜평균 5.375 s12.87 GiB
체크포인트 3종 순환78.5 / 76.0 / 88.6 s12.88 GiB
after웜평균 5.286 s12.89 GiB

0.98배. 아무 일도 안 일어났습니다. VRAM은 0.02GiB 늘었을 뿐이고요. ComfyUI가 이전 모델을 멀쩡히 내리고 있었습니다. 첫 번째 설명은 여기서 무너졌습니다.

실험 3 — 영상이 원인일까 (양성, 하지만 성급했다)

포화 상태의 피크가 30GiB였는데 SDXL은 12.87GiB에서 안 올라갑니다. 카드를 꽉 채우는 작업은 영상뿐이라고 봤고, 가운데에 WAN 2.2를 한 번만 끼웠습니다.

단계시간VRAM
before웜평균 5.666 s12.87 GiB
WAN 2.2 480p 33프레임 1회374.0 s27.54 GiB
after웜평균 19.115 s27.54 GiB

3.37배. 재현됐길래 여기서 "영상 워크로드가 원인"이라고 적었습니다. 그게 성급했습니다. 이 실험에서 before와 after 사이에 달라진 게 셋이었거든요. 작업이 영상이라는 점, 할당이 27GiB로 크다는 점, 모델이 4개나 올라왔다는 점. 저는 그중 첫 번째만 집었습니다.

실험 4 — 영상이 아니라 크기였다

영상을 빼고 크기만 남기면 갈립니다. 전 구간 sd_xl_base 하나만 쓰고, 가운데에 2048×2048 한 장만 끼웠습니다. 모델 교체가 아예 없는 구성입니다.

단계시간VRAM
before웜평균 5.366 s12.87 GiB
2048×2048 1장 (피크 26.11 GiB)18.2 s26.11 GiB
after웜평균 14.633 s26.20 GiB

2.73배. 모델은 처음부터 끝까지 하나였는데도 같은 일이 벌어졌습니다. 영상이 특별한 게 아니라 큰 할당이 특별했던 겁니다. 영상은 그저 큰 할당을 가장 쉽게 만드는 작업이었을 뿐이고, 실험 2에서 아무 일도 없었던 것도 최고수위가 12.87GiB에서 안 올라갔기 때문입니다.

메모리는 어디에 갇혀 있나

/free를 부른 직후에 세 층의 수치를 나란히 봤습니다.

드라이버 회계        26.2 GiB 사용 중
torch_vram_total      0.14 GiB   ← torch는 거의 안 잡고 있다고 보고
ComfyUI가 보는 여유    5.78 GiB

torch는 다 반납했다는데 드라이버는 26.2GiB를 여전히 이 프로세스 것으로 셉니다. 메모리가 torch 아래층에 갇혀 있는 거죠. /free가 200을 주면서 아무것도 안 하는 것처럼 보였던 이유도 이겁니다. 거짓말이 아니라 자기 층에서는 진짜로 해제한 거였어요. 그리고 ComfyUI는 남은 여유를 5.78GiB로 보고 모델 상주 여부를 판단하니, 계속 내렸다 올리게 됩니다.

실험 5 — 설정 하나였다

그 아래층을 건드리는 설정이 하나 있습니다. 제 launch.sh에 이렇게 들어 있었습니다.

# Reduce VRAM fragmentation (matters for video-model latents and model swapping)
export PYTORCH_HIP_ALLOC_CONF=expandable_segments:True

도움 되라고 넣은 줄입니다. 끄고 실험 4를 그대로 다시 돌렸습니다.

설정저하스파이크 후 VRAM/free
expandable_segments 켬, reserve 1 (기존)2.73배+13.34 GiB변화 없음
expandable_segments , reserve 11.06배−1.66 GiB26.95 → 0.57 GiB
expandable_segments 켬, reserve 63.6배+13.49 GiB변화 없음

끄니까 셋이 한꺼번에 해결됩니다. 저하가 사라지고(1.06배는 측정 노이즈), 큰 작업이 끝나면 VRAM이 도로 내려가고, /free가 진짜로 듣습니다. 26.95GiB가 0.57GiB로 돌아갑니다.

--reserve-vram을 1에서 6으로 올린 쪽은 의외였습니다. 여유를 크게 잡아두면 나아질 줄 알았는데 2.73배가 3.6배로 악화됐습니다. 예약한 만큼 실제로 쓸 공간이 줄어서 더 심하게 스래싱하는 것으로 보입니다.

그래서 어떻게

이미지와 영상을 섞어 쓰시고 위 증상이 있다면 expandable_segments를 빼보세요. 저는 launch.sh에서 그 export 한 줄을 주석 처리했습니다.

다만 그냥 끄라고 말하기는 조심스럽습니다. 저 옵션은 원래 단편화를 줄이려고 넣은 것이고, 제가 앞서 잰 WAN 2.2 720p는 31.8GiB로 카드 한계에 붙어서 돌았습니다. 그게 이 옵션 덕에 겨우 들어간 것일 수도 있는데 확인을 못 했습니다. 끄고 나서 본인이 쓰는 가장 큰 영상 워크로드가 여전히 들어가는지 꼭 확인하세요. OOM이 나면 되돌리시고요.

끄는 게 부담스러우면 재시작이 여전히 확실한 답입니다. 5초 걸리고 전부 돌려받습니다.

systemctl --user restart comfyui.service
# 전  32,184,479,744 B (29.97 GiB)
# 후      59,969,536 B ( 0.06 GiB)

확인은 AMD 기준으로 이 파일이 정확합니다. rocm-smi --showpids의 프로세스별 집계는 실제와 어긋날 수 있습니다.

watch -n 5 'echo $(( $(cat /sys/class/drm/card0/device/mem_info_vram_used) / 1024 / 1024 )) MiB'

정정 이력

이 글은 원인을 두 번 잘못 짚었습니다. 지운 게 아니라 위 실험 2·3에 그대로 남겨뒀습니다.

  • 첫 번째 — "큰 모델을 오가면 잔여물이 쌓인다"고 썼습니다. 실험 2에서 0.98배로 반증됐습니다.
  • 두 번째 — "영상 워크로드가 원인"이라고 썼습니다. 실험 4에서 이미지만으로도 2.73배가 나오면서 반증됐습니다. 교란 변수 세 개 중 하나만 보고 결론을 낸 게 잘못이었습니다.

세 번째 결론도 틀릴 수 있습니다. 지금까지 확인한 건 카드 하나, ROCm 하나, 워크로드 몇 개뿐입니다.

한계

  • R9700 한 대, ROCm 7.2.4 한 버전입니다. NVIDIA의 expandable_segments에서도 같은지는 모릅니다. 같은 옵션이 CUDA 쪽에도 있으니 확인해볼 만합니다.
  • 실험 3·4는 20스텝 총시간으로 쟀습니다. 실험 1처럼 차분으로 샘플링과 오버헤드를 분리하지 않았으니 2.73배·3.37배는 총시간 기준입니다.
  • 끈 상태에서 대형 영상 워크로드가 여전히 들어가는지는 확인 못 했습니다. 위에 적은 대로입니다.
  • 단편화가 실제로 문제가 되는 장시간 세션은 재보지 않았습니다. 저 옵션이 원래 노리던 이득이 어떤 상황에서 나오는지는 여전히 모릅니다.

같이 재주실 분

절차는 간단합니다. ComfyUI 재시작 → 1024 워크플로우 3회 → 2048 한 장 → 같은 1024 워크플로우 3회. 첫 회차는 로드가 섞이니 빼고 나머지 평균을 비교하시면 됩니다.

NVIDIA에서 같은 일이 생기는지가 제일 궁금합니다. PYTORCH_CUDA_ALLOC_CONF에도 같은 옵션이 있으니 켠 상태와 끈 상태를 비교해보시면 바로 나올 겁니다. 배수만 댓글로 남겨주셔도 비교표가 됩니다. "우리 환경에선 안 그런데요"도 그 자체로 중요한 정보고요.