- Published on
Stable Diffusion VRAM OOM, xFormers·Tiled VAE로 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
Stable Diffusion을 돌리다 보면 가장 자주 마주치는 에러가 VRAM OOM(Out Of Memory)입니다. 특히 1024x1024 이상, 배치 크기 증가, ControlNet/LoRA 여러 개 동시 사용, 고해상도 업스케일(Hires fix)까지 얹으면 8GB~12GB VRAM은 금방 한계에 닿습니다.
이 글에서는 “왜 OOM이 나는지”를 어텐션과 VAE 관점에서 짚고, 효과가 큰 두 가지 카드인 xFormers(메모리 효율 어텐션)와 Tiled VAE(타일 기반 디코딩)로 체감 VRAM 사용량을 낮추는 방법을 정리합니다. 마지막에는 WebUI(AUTOMATIC1111), ComfyUI, Diffusers 각각에서 바로 적용 가능한 설정/코드까지 제공합니다.
참고로 VRAM OOM은 GPU 메모리 문제지만, 시스템 메모리 부족이나 프로세스 강제 종료와 증상이 섞여 보일 때도 있습니다. 리눅스에서 프로세스가 죽는 원인을 OS 레벨에서 추적하는 방법은 리눅스 OOM Killer로 프로세스 죽음 원인 추적도 함께 보면 진단이 빨라집니다.
Stable Diffusion에서 VRAM이 터지는 지점 3가지
1) 어텐션(Attention) 메모리 폭증
U-Net의 크로스 어텐션은 토큰과 공간(feature map) 사이 상호작용을 계산합니다. 해상도가 올라가면 feature map의 공간 차원이 커지고, 어텐션 내부에서 다루는 텐서가 커지면서 VRAM이 급격히 증가합니다.
특히 다음 조합에서 잘 터집니다.
1024x1024이상 + 스텝 증가- 배치 크기
batch_size증가 - ControlNet 여러 개 + 높은 해상도
- SDXL(기본 모델 자체가 큼)
여기서 xFormers가 큰 역할을 합니다. 일반 어텐션 구현 대비 메모리 효율이 높은 커널(주로 memory-efficient attention)을 사용해서 VRAM 사용량을 줄입니다.
2) VAE 디코딩(잠복 공간 latent → 이미지)에서 피크 발생
샘플링이 끝난 뒤 VAE로 이미지를 디코딩할 때도 꽤 큰 텐서가 필요합니다. 특히 고해상도 결과를 한 번에 디코딩하려고 하면 마지막 순간에 VRAM 피크가 생겨 OOM이 나기도 합니다.
이때 Tiled VAE는 이미지를 여러 타일로 쪼개 디코딩하고 다시 합치는 방식으로 피크 메모리를 낮춥니다. 속도는 약간 느려질 수 있지만 “마지막에 터지는 OOM”을 잡는 데 매우 효과적입니다.
3) Hires fix / 업스케일 파이프라인이 사실상 2회 생성
Hires fix는 보통
- 낮은 해상도로 1차 생성
- 업스케일 후 2차 디노이즈
처럼 동작합니다. 즉, U-Net을 사실상 두 번 돌리는 구조라 VRAM 여유가 없으면 OOM이 더 쉽게 납니다. 이때도 어텐션 최적화(xFormers) + 디코딩 최적화(Tiled VAE) 조합이 효과적입니다.
해결 전략 요약: 어디에 무엇을 적용할까
| 증상 | 원인 후보 | 1순위 해결 | 2순위 해결 |
|---|---|---|---|
| 샘플링 중간에 OOM | U-Net 어텐션 | xFormers | 해상도/배치/스텝 조정, --medvram |
| 샘플링은 끝나는데 마지막에 OOM | VAE 디코딩 피크 | Tiled VAE | VAE를 FP16, --lowvram |
| SDXL + ControlNet에서 자주 OOM | 모델 자체 큼 + 어텐션 증가 | xFormers | ControlNet 수/해상도 감소 |
xFormers: 메모리 효율 어텐션으로 VRAM 절약
xFormers가 하는 일
xFormers는 어텐션 계산을 더 VRAM 친화적으로 수행합니다. 결과적으로 다음이 개선됩니다.
- 동일 해상도에서 VRAM 사용량 감소
- OOM이 나던 해상도에서 샘플링 가능해짐
- 일부 환경에서는 속도도 개선(환경에 따라 상이)
다만 GPU/드라이버/파이토치 조합에 따라 설치가 까다롭거나, 특정 커널에서 호환성 문제가 생길 수 있습니다. 그래도 “OOM 해결” 관점에서는 가장 먼저 시도할 가치가 있습니다.
AUTOMATIC1111(WebUI)에서 xFormers 켜기
가장 흔한 방법은 실행 옵션에 --xformers를 붙이는 것입니다.
# 예시: webui-user.sh 또는 webui-user.bat의 COMMANDLINE_ARGS
COMMANDLINE_ARGS="--xformers"
실행 후 콘솔 로그에 xFormers가 로드되었다는 메시지가 보이면 적용된 것입니다.
체크 포인트
- xFormers가 제대로 설치되지 않으면 “로드 실패”가 나거나, 그냥 무시되고 기본 어텐션으로 돌아갑니다.
- 파이토치 버전과 CUDA 버전이 맞지 않으면 설치가 꼬일 수 있습니다.
Diffusers에서 xFormers 활성화
파이프라인에서 아래처럼 켭니다.
import torch
from diffusers import StableDiffusionPipeline
pipe = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=torch.float16,
).to("cuda")
pipe.enable_xformers_memory_efficient_attention()
image = pipe(
prompt="a cinematic portrait photo",
num_inference_steps=28,
).images[0]
image.save("out.png")
만약 환경에 따라 xFormers가 불가하면 예외가 날 수 있으니, 서비스 코드에서는 try-catch로 폴백을 두는 편이 안전합니다.
try:
pipe.enable_xformers_memory_efficient_attention()
except Exception as e:
print("xFormers enable failed:", e)
Tiled VAE: 디코딩을 타일로 쪼개 피크 VRAM 줄이기
Tiled VAE가 특히 유효한 순간
- 샘플링(디노이즈)은 끝나는데 저장 직전에 OOM
- SDXL에서
1024x1024이상 결과를 자주 뽑아야 함 - 업스케일/후처리 파이프라인에서 최종 디코딩이 병목
VAE 디코딩은 “한 번에 큰 텐서”를 만들기 쉬워서 피크 메모리가 높습니다. 타일링은 이를 분할 정복해 피크를 낮춥니다.
AUTOMATIC1111(WebUI)에서 Tiled VAE 사용
WebUI에서는 확장(Extension) 또는 내장 옵션(버전에 따라 다름)으로 타일 VAE를 켤 수 있습니다.
- Extensions 탭에서
Tiled VAE관련 확장을 설치 - Settings에서 VAE 타일 크기(tile size)와 오버랩(overlap)을 조정
권장 튜닝 방향:
- 타일 크기: VRAM이 빡빡하면 작게(예:
256), 여유가 있으면 크게(예:512) - 오버랩: 경계 이음새(seam) 방지를 위해 소량(예:
32또는64)
타일 크기가 너무 작으면 속도가 느려지고, 오버랩이 너무 작으면 경계가 티 날 수 있습니다. “OOM이 안 나는 최소 타일 크기”를 먼저 찾고, 그 다음 품질과 속도를 맞추는 순서가 좋습니다.
Diffusers에서 VAE 타일링 적용
Diffusers는 VAE 타일링을 위한 헬퍼 메서드를 제공하는 경우가 있습니다(버전에 따라 API가 다를 수 있음). 개념적으로는 아래처럼 “VAE 디코더를 타일 모드로” 바꿉니다.
import torch
from diffusers import StableDiffusionXLPipeline
pipe = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16,
).to("cuda")
# 어텐션 메모리 절약
try:
pipe.enable_xformers_memory_efficient_attention()
except Exception:
pass
# VAE 디코딩 피크 절약(타일링)
# 환경에 따라 메서드명이 다를 수 있어, 사용 중인 diffusers 문서/버전에 맞춰 확인 필요
pipe.enable_vae_tiling()
image = pipe(
prompt="ultra detailed landscape, 8k",
num_inference_steps=30,
width=1024,
height=1024,
).images[0]
image.save("sxl.png")
만약 enable_vae_tiling()이 없다면 diffusers 버전이 낮을 수 있습니다. 이 경우 업그레이드하거나, 커스텀 타일 디코딩 로직을 적용해야 합니다.
함께 쓰면 좋은 추가 옵션(효과 대비 부작용 적은 순)
1) FP16 / BF16로 통일
대부분의 OOM은 “그냥 텐서가 크다”가 본질이라, dtype을 줄이는 게 즉효입니다.
- NVIDIA GPU면 보통
FP16이 가장 흔한 선택 - 최신 GPU에서는
BF16이 안정적인 경우도 있음
Diffusers 예시(이미 위에서 사용): torch_dtype=torch.float16
2) 배치 크기와 해상도: VRAM에 가장 정직한 레버
batch_size를1로width/height를 한 단계 낮추고 업스케일로 보정
해상도는 어텐션 메모리에 직접 타격이라, 1024에서 896으로만 내려도 체감이 큽니다.
3) WebUI의 --medvram, --lowvram
WebUI는 VRAM이 작은 카드에서 레이어를 더 공격적으로 스왑/분할해 VRAM을 아끼는 옵션이 있습니다.
COMMANDLINE_ARGS="--xformers --medvram"
대신 속도는 느려질 수 있습니다. “속도보다 일단 안 죽는 게 우선”일 때 유용합니다.
4) ControlNet/LoRA 동시 사용 개수 줄이기
ControlNet은 추가 네트워크를 더 얹는 형태라 VRAM을 더 먹습니다. LoRA도 여러 개를 동시에 올리면 누적 비용이 커집니다.
- ControlNet은 꼭 필요한 것만
- LoRA는 가중치/개수 최소화
실전 진단: OOM이 어디서 나는지 빠르게 확인하는 법
콘솔 로그로 구간 파악
- “sampling step” 중간에 터지면 보통 U-Net/어텐션
- “decode” 또는 저장 직전에 터지면 VAE 디코딩 피크
이 구분만 해도 xFormers를 먼저 켤지, Tiled VAE를 먼저 켤지 우선순위가 잡힙니다.
nvidia-smi로 피크 타이밍 보기
터미널에서 아래처럼 모니터링하면 “어느 순간 치솟는지”가 보입니다.
watch -n 0.5 nvidia-smi
만약 컨테이너/서버 환경에서 모니터링을 자동화하고 싶다면, 메모리/리소스 누수나 폭증을 다루는 관점에서 AutoGPT 메모리 누수 막는 벡터DB TTL·압축처럼 “사용량을 관측하고 상한을 두는” 패턴이 의외로 도움이 됩니다(물론 VRAM 자체는 TTL로 해결되지 않지만, 파이프라인 운영 관점에서 리소스 관리 사고방식이 유사합니다).
추천 조합(VRAM 용량별)
8GB VRAM
- WebUI:
--xformers --medvram+ Tiled VAE - 해상도:
512~768위주, SDXL은 타협 필요
12GB VRAM
--xformers+ Tiled VAE면 SDXL1024도 상황에 따라 가능- ControlNet 다중 사용 시에는 타일/옵션 조정 필수
16GB 이상
- 대부분의 OOM은 사라지지만, 초고해상도/대형 배치/다중 ControlNet에서는 여전히 피크가 생김
- 품질 욕심(업스케일, 디테일러, 후처리)이 늘수록 Tiled VAE가 “안전장치”가 됨
문제 해결 체크리스트
- OOM 발생 시점이 샘플링 중인지, 디코딩/저장 직전인지 구분
- 샘플링 중 OOM이면
xFormers부터 적용 - 마지막 디코딩에서 OOM이면
Tiled VAE를 우선 적용 - 그래도 터지면 해상도, 배치, ControlNet/LoRA 개수부터 줄이기
- WebUI라면
--medvram또는--lowvram로 생존성 확보
마무리
xFormers와 Tiled VAE는 “Stable Diffusion OOM”을 가장 비용 대비 효과적으로 줄이는 조합입니다. 핵심은 둘이 해결하는 지점이 다르다는 것입니다.
xFormers: U-Net 어텐션의 상시 메모리 사용량을 낮춤Tiled VAE: VAE 디코딩의 피크 메모리를 낮춤
둘을 함께 적용하면, 기존에 768에서만 돌던 워크플로우가 1024로 올라가거나, SDXL + 후처리까지 한 번에 끝내는 구성이 가능해집니다. 다음 단계로는 “내 워크플로우에서 어떤 단계가 VRAM을 가장 많이 먹는지”를 측정해, 옵션을 최소 변경으로 최적화하는 쪽으로 튜닝해보면 좋습니다.