서버를 며칠 켜둔 채 쓰다 보면 어느 순간 생성이 굼떠집니다. 재시작하면 돌아오고요. 원인을 찾는 데 실험을 다섯 번 했고, 그 사이에 제가 앞서 공개한 원인이 두 번 뒤집혔습니다. 과정을 그대로 남깁니다. 틀린 쪽도 같이요.
결론
- 큰 작업을 한 번 돌리면 그 뒤 같은 워크로드가 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 s | 5.66 s | 4.01배 |
| 40스텝 전체 | 37.59 s | 9.95 s | 3.78배 |
| 고정 오버헤드 | 7.76 s | 1.37 s | 5.65배 |
| VRAM 피크 | 30.25 GiB | 14.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 s | 12.87 GiB |
| 체크포인트 3종 순환 | 78.5 / 76.0 / 88.6 s | 12.88 GiB |
| after | 웜평균 5.286 s | 12.89 GiB |
0.98배. 아무 일도 안 일어났습니다. VRAM은 0.02GiB 늘었을 뿐이고요. ComfyUI가 이전 모델을 멀쩡히 내리고 있었습니다. 첫 번째 설명은 여기서 무너졌습니다.
실험 3 — 영상이 원인일까 (양성, 하지만 성급했다)
포화 상태의 피크가 30GiB였는데 SDXL은 12.87GiB에서 안 올라갑니다. 카드를 꽉 채우는 작업은 영상뿐이라고 봤고, 가운데에 WAN 2.2를 한 번만 끼웠습니다.
| 단계 | 시간 | VRAM |
|---|---|---|
| before | 웜평균 5.666 s | 12.87 GiB |
| WAN 2.2 480p 33프레임 1회 | 374.0 s | 27.54 GiB |
| after | 웜평균 19.115 s | 27.54 GiB |
3.37배. 재현됐길래 여기서 "영상 워크로드가 원인"이라고 적었습니다. 그게 성급했습니다. 이 실험에서 before와 after 사이에 달라진 게 셋이었거든요. 작업이 영상이라는 점, 할당이 27GiB로 크다는 점, 모델이 4개나 올라왔다는 점. 저는 그중 첫 번째만 집었습니다.
실험 4 — 영상이 아니라 크기였다
영상을 빼고 크기만 남기면 갈립니다. 전 구간 sd_xl_base 하나만 쓰고, 가운데에 2048×2048 한 장만 끼웠습니다. 모델 교체가 아예 없는 구성입니다.
| 단계 | 시간 | VRAM |
|---|---|---|
| before | 웜평균 5.366 s | 12.87 GiB |
| 2048×2048 1장 (피크 26.11 GiB) | 18.2 s | 26.11 GiB |
| after | 웜평균 14.633 s | 26.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 1 | 1.06배 | −1.66 GiB | 26.95 → 0.57 GiB |
| expandable_segments 켬, reserve 6 | 3.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에도
같은 옵션이 있으니 켠 상태와 끈 상태를 비교해보시면 바로 나올 겁니다. 배수만 댓글로 남겨주셔도
비교표가 됩니다. "우리 환경에선 안 그런데요"도 그 자체로 중요한 정보고요.