- Published on
Stable Diffusion VRAM OOM, xFormers·Tiling 최적화
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
Stable Diffusion을 로컬 GPU에서 돌리다 보면 가장 흔하게 마주치는 벽이 CUDA out of memory 입니다. 특히 1024x1024 이상, Hires.fix, ControlNet, 여러 LoRA/텍스트 인버전, 고스텝 샘플링을 겹치면 VRAM이 급격히 튑니다.
이 글은 “왜 OOM이 나는지”를 메모리 관점에서 쪼개고, 효과가 큰 두 축인 xFormers(메모리 효율 어텐션)와 Tiling(VAE/Latent 타일링)을 중심으로, 품질을 크게 해치지 않으면서 VRAM을 줄이는 실전 설정을 제시합니다.
관련해서 로컬 LLM 최적화에서 OOM을 다루는 글도 함께 보면 메모리 병목을 보는 감이 빨리 잡힙니다: Transformers 로컬 LLM 느림·OOM, 4bit+FlashAttn2
VRAM OOM이 나는 “진짜” 지점들
Stable Diffusion 파이프라인은 대략 다음 덩어리로 VRAM을 씁니다.
- UNet 추론(확산 과정): 스텝마다 반복 실행되며, 어텐션과 피처맵이 크게 잡아먹습니다.
- 텍스트 인코더(CLIP): 프롬프트 길이/배치에 따라 메모리 사용이 늘지만, 보통 UNet보다 영향이 작습니다.
- VAE 디코드/인코드: 최종 이미지로 변환하는 구간에서 순간적으로 피크가 뜨는 경우가 많습니다.
- 부가 모듈: ControlNet, IP-Adapter, 여러 LoRA, 업스케일/후처리 등이 누적됩니다.
OOM은 “평균 사용량”이 아니라 피크(peak) 에서 터집니다. 그래서 최적화는 대개 피크를 깎는 방향으로 접근해야 합니다.
해상도가 VRAM에 미치는 영향: 거의 제곱으로 증가
확산 모델은 이미지 공간이 아니라 latent 공간(보통 H/8 x W/8)에서 돌아가지만, 해상도를 올리면 피처맵 크기가 같이 커집니다. 대략적으로 VRAM은 H x W에 비례해 증가하고, 어텐션/피처맵 누적 때문에 체감은 더 가파르게 느껴집니다.
예를 들어 512에서 잘 되던 설정이 768이나 1024로 가면, 단순히 2배가 아니라 훨씬 더 크게 튑니다. 게다가 batch size를 올리면 곱으로 증가합니다.
1) xFormers: 메모리 효율 어텐션으로 UNet 피크 낮추기
xFormers는 어텐션 계산을 더 메모리 효율적인 커널로 바꿔서, 특히 고해상도에서 VRAM 피크를 크게 줄여줍니다. 체감상 “같은 VRAM에서 해상도를 한 단계 올릴 수 있게” 해주는 경우가 많습니다.
어떤 상황에서 특히 효과가 큰가
768이상 고해상도- ControlNet 등으로 UNet이 무거워진 상황
- 배치가
2이상이거나, 여러 장을 한 번에 뽑는 경우
설치/활성화(Automatic1111 기준)
환경마다 다르지만, 보통은 WebUI 실행 인자에서 켜고 끌 수 있습니다.
# 예시: webui-user.sh 또는 실행 커맨드에 추가
# xFormers 활성화
python launch.py --xformers
만약 드라이버/파이토치/쿠다 조합이 맞지 않아 설치가 꼬이면, “성능보다 안정성”을 우선해 PyTorch 버전과 CUDA 런타임을 맞추는 것이 중요합니다. xFormers는 커널 호환성 이슈가 잦아서, 되는 조합을 고정해두는 게 운영 비용이 낮습니다.
xFormers를 켰는데도 OOM이면
xFormers는 주로 어텐션 메모리를 줄입니다. 그런데 OOM이 VAE 디코드 순간 피크에서 터지거나, ControlNet이 너무 많거나, Hires.fix가 과도하면 여전히 터질 수 있습니다. 이때는 아래의 Tiling이 훨씬 직접적으로 먹힙니다.
2) Tiling: VAE/Latent 타일링으로 “순간 피크”를 분할
타일링은 큰 텐서를 한 번에 처리하지 않고, 여러 타일로 쪼개서 순차 처리하는 방식입니다. 장점은 명확합니다.
- VRAM 피크를 강하게 낮춤
- 특히
VAE 디코드에서 OOM이 나는 케이스에 특효
대신 단점도 있습니다.
- 타일 경계에서 미세한 이음새(seam)가 생길 수 있음
- 속도가 느려질 수 있음
하지만 적절한 overlap(겹침)과 타일 크기 선택으로 seam을 상당히 억제할 수 있습니다.
A1111에서 흔한 타일링 옵션들
구성에 따라 메뉴 이름이 조금씩 다르지만, 흔히 다음이 등장합니다.
VAE tiling또는Tiled VAELatent tiling(확산 과정 자체를 타일링)
실전에서는 보통 VAE 타일링만으로도 OOM이 해결되는 경우가 많고, 그래도 안 되면 latent 타일링까지 고려합니다.
diffusers에서 VAE 타일링 예시 코드
WebUI가 아니라 파이썬에서 diffusers로 파이프라인을 돌리는 경우, VAE 타일링을 코드로 켤 수 있습니다.
import torch
from diffusers import StableDiffusionPipeline
model_id = "runwayml/stable-diffusion-v1-5"
pipe = StableDiffusionPipeline.from_pretrained(
model_id,
torch_dtype=torch.float16,
)
pipe = pipe.to("cuda")
# VAE 타일링 활성화: 큰 해상도에서 VRAM 피크를 낮춤
pipe.vae.enable_tiling()
# 메모리 절약 옵션(상황에 따라)
pipe.enable_attention_slicing()
image = pipe(
prompt="a photo of a cat, ultra detailed",
height=1024,
width=1024,
num_inference_steps=30,
).images[0]
image.save("out.png")
주의할 점은, 타일링은 “무조건 켜면 좋은 스위치”라기보다 OOM이 나는 구간을 특정해서 쓰는 도구입니다. 예를 들어 512 위주 작업에서는 오히려 속도만 느려질 수 있습니다.
xFormers와 Tiling을 같이 쓸 때의 우선순위
현장에서 추천하는 적용 순서는 다음입니다.
- xFormers 활성화: UNet 어텐션 피크 절감
- VAE 타일링 활성화: 디코드 피크 절감
- 그래도 안 되면 latent 타일링 또는 해상도/배치 감소
즉, xFormers는 “상시 켜두는 편익”이 크고, 타일링은 “필요할 때 피크를 쪼개는 응급 처치” 성격이 강합니다.
함께 적용하면 좋은 메모리 절약 옵션(부작용 포함)
1) --medvram / --lowvram (WebUI)
VRAM이 작은 GPU에서 효과가 있지만, 속도 저하가 큽니다.
python launch.py --xformers --medvram
--medvram: 타협점--lowvram: 정말 안 될 때만
2) Half precision(fp16) / bf16
대부분의 컨슈머 GPU에서는 fp16이 기본 선택입니다. 다만 특정 GPU/드라이버 조합에서 bf16이 더 안정적일 수 있습니다.
- 장점: VRAM 절감, 속도 개선 가능
- 단점: 일부 설정에서 NaN/검은 이미지 등 수치 불안정 가능
3) Attention slicing
어텐션을 슬라이스로 나눠 계산해서 VRAM을 줄입니다.
- 장점: VRAM 감소
- 단점: 속도 감소
diffusers에서는 위 코드처럼 pipe.enable_attention_slicing()로 켭니다.
4) 배치/동시 생성 수 줄이기
batch size 또는 한 번에 뽑는 이미지 수가 2 이상이면 VRAM이 곱으로 늘어납니다. OOM이 난다면 가장 먼저 1로 고정해보는 게 빠릅니다.
5) Hires.fix의 함정
Hires.fix는 “두 번 생성”에 가깝습니다.
- 1차: 낮은 해상도 latent 생성
- 2차: 업스케일 후 추가 디노이즈
2차 단계에서 UNet/VAE가 다시 돌면서 피크가 튀기 때문에, OOM의 주범이 되기 쉽습니다. 해결은 다음 중 하나입니다.
- 업스케일 배율을 낮추기
- denoise strength를 조절해 스텝/부하 줄이기
- VAE 타일링 켜기
- 아예 외부 업스케일러로 분리(후처리 파이프라인)
OOM을 “재현 가능하게” 잡는 체크리스트
OOM은 설정 조합이 많아 감으로 잡으면 시간이 오래 걸립니다. 아래 순서로 “한 번에 하나씩” 바꾸면 원인 추적이 빨라집니다.
- 해상도 고정: 예를 들어
1024x1024로 고정 - batch size 고정:
1 - ControlNet/LoRA 전부 끄기: 순정으로 테스트
- xFormers on/off 비교
- VAE 타일링 on/off 비교
- 그래도 터지면 Hires.fix/업스케일 단계 분리
이 과정은 웹 성능에서 Long Task를 쪼개 원인을 특정하는 방식과 유사합니다. 문제를 덩어리로 보지 말고 “피크를 만드는 구간”을 분해해 확인하는 게 핵심입니다: Chrome INP 급등 원인 - Long Task 분해 실전
실전 권장 프리셋(대략적인 가이드)
GPU VRAM에 따라 “일단 성공”을 목표로 하는 조합을 제안합니다. 모델/ControlNet/샘플러에 따라 달라지니, 아래는 출발점으로만 보세요.
8GB VRAM
- xFormers: 켬
- 해상도:
512또는768(상황에 따라) - Hires.fix: 가능하면 피하거나 보수적으로
- VAE 타일링:
768이상 또는 OOM 시 켬 - ControlNet: 1개부터 시작
12GB VRAM
- xFormers: 켬
- 해상도:
768안정권,1024는 설정에 따라 - VAE 타일링:
1024에서 OOM 시 켬 - Hires.fix: 업스케일 배율/스텝을 보수적으로
24GB VRAM
- 대부분의 경우 xFormers만으로도 충분
- 다만
1536이상, 다중 ControlNet, 대형 배치에서는 타일링이 여전히 의미 있음
품질 저하를 최소화하는 타일링 팁
타일링에서 가장 신경 써야 하는 건 경계 이음새입니다.
- 타일 크기를 너무 작게 하지 않기
- 가능하면 overlap(겹침) 옵션을 키기
- seam이 보이면 VAE 타일링만 켜고 latent 타일링은 끄는 방향부터 시도
또한 후처리에서 약한 디노이즈나 블러가 seam을 숨기는 데 도움이 되기도 하지만, 과하면 디테일이 죽으니 최소한으로만 적용하는 게 좋습니다.
결론: OOM은 “피크를 깎는 게임”이다
Stable Diffusion의 VRAM OOM은 단순히 “VRAM이 부족해서”라기보다, 특정 단계에서 피처맵/어텐션/디코드가 겹치며 순간 피크가 터지는 문제인 경우가 많습니다.
- UNet 어텐션 피크는
xFormers로 먼저 깎고 - VAE 디코드 피크는
Tiling으로 쪼개고 - 그래도 안 되면 배치, Hires.fix, ControlNet 수를 줄여 “피크의 곱”을 줄이는 것이 정석입니다.
이 순서대로 적용하면, 같은 GPU에서도 한 단계 높은 해상도/복잡한 파이프라인을 훨씬 안정적으로 굴릴 수 있습니다.