Published on

Stable Diffusion VRAM OOM - xFormers·VAE 타일링

Authors

Stable Diffusion을 돌리다 보면 가장 흔하게 마주치는 에러가 CUDA out of memory(VRAM OOM)입니다. 특히 해상도를 올리거나(예: 1024x1024), 배치 크기를 늘리거나, 고해상도 보정(Hires. fix)을 켜는 순간 메모리 피크가 튀면서 바로 터집니다.

이 글은 “왜 OOM이 나는지”를 모델 구조 관점에서 짚고, 가장 효과가 큰 두 축인 **xFormers(메모리 효율 Attention)**와 **VAE 타일링(디코드/인코드 타일 분할)**로 VRAM 피크를 낮추는 방법을 정리합니다. WebUI(AUTOMATIC1111), ComfyUI, diffusers 모두에 적용 가능한 개념 위주로 설명하고, 바로 복붙 가능한 커맨드/설정 예시를 포함합니다.

관련해서 로컬 LLM에서의 OOM 대응(4bit, 오프로딩)도 원리가 비슷하니 함께 읽어두면 도움이 됩니다: Transformers 로컬 LLM OOM - 4bit+오프로딩

VRAM OOM이 터지는 “진짜” 지점 2곳

Stable Diffusion 파이프라인에서 VRAM을 크게 먹는 구간은 크게 두 가지입니다.

1) U-Net의 Attention (특히 고해상도)

이미지 생성의 대부분 시간과 메모리는 U-Net에서 발생합니다. 해상도가 커질수록 feature map이 커지고, Attention의 중간 텐서가 급격히 늘어납니다. 이때 메모리 사용량은 대략적으로

  • 해상도 H x W 증가
  • 샘플링 스텝 증가
  • 배치 크기 증가

에 따라 피크가 상승합니다.

여기서 xFormers는 Attention 계산 방식을 바꿔 중간 텐서를 덜 잡아먹게 만들어, 같은 해상도에서도 VRAM 피크를 유의미하게 낮춰줍니다.

2) VAE 디코드/인코드 (이미지로 변환하는 구간)

U-Net이 latent 공간에서 작업을 끝내면, 마지막에 VAE가 latent를 픽셀 이미지로 디코드합니다. 이 디코드가 해상도에 정비례로 무거워서, 고해상도나 배치가 커지면 마지막 단계에서 OOM이 나기도 합니다.

VAE 타일링은 이 디코드를 한 번에 하지 않고 타일로 쪼개서 순차 처리해 VRAM 피크를 낮춥니다. 대신 약간의 속도 손해가 있을 수 있습니다.

증상으로 원인 구분하기

OOM이 났을 때 로그를 보면 대략 어느 지점에서 터졌는지 감이 옵니다.

  • 샘플링 중간(예: Step 12/30)에서 터짐: U-Net/Attention 쪽 가능성이 큼 → xFormers, 해상도/배치/스텝 조정이 우선
  • 거의 끝(예: VAE decode 직전/직후)에서 터짐: VAE 쪽 가능성이 큼 → VAE 타일링, VAE를 CPU로, --no-half-vae 조합 검토

운영에서 장애를 “증상 기반으로 분류”하는 습관은 다른 영역에서도 매우 유효합니다. 예를 들어 K8s에서 재시작 루프를 원인별로 쪼개는 방식과 유사합니다: K8s CrashLoopBackOff 원인별 로그·Probe 해결 가이드

xFormers: 가장 먼저 켜볼 스위치

xFormers는 메모리 효율적인 Attention 구현을 제공하는 라이브러리입니다. Stable Diffusion에서는 “Attention의 중간 텐서를 덜 만들고, 더 스트리밍 방식으로 계산”하는 효과로 VRAM 사용량이 줄어드는 경우가 많습니다.

AUTOMATIC1111 WebUI에서 xFormers 활성화

가장 흔한 방법은 실행 옵션에 --xformers를 추가하는 것입니다.

# 예시: webui-user.sh 또는 실행 커맨드에 추가
./webui.sh --xformers

Windows라면 webui-user.batCOMMANDLINE_ARGS에 넣는 방식이 일반적입니다.

set COMMANDLINE_ARGS=--xformers

체크 포인트

  • xFormers는 GPU/드라이버/CUDA 조합에 따라 설치가 까다롭거나, 특정 버전에서 불안정할 수 있습니다.
  • 에러가 나면 “xFormers 자체 설치 실패”인지 “런타임에서 attention kernel 실패”인지 로그를 분리해서 보세요.

diffusers에서 xFormers 활성화

Hugging Face diffusers를 직접 쓰는 경우라면 파이프라인에 다음을 적용합니다.

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()

주의

  • PyTorch 버전과 xFormers 빌드가 안 맞으면 enable_xformers_memory_efficient_attention()에서 예외가 날 수 있습니다.
  • 이 경우 대안으로 PyTorch의 scaled_dot_product_attention 기반 최적화(일명 SDPA)가 더 잘 맞는 환경도 있습니다.

VAE 타일링: “마지막에 터지는 OOM”을 잡는 방법

VAE는 latent를 픽셀로 바꾸는 디코더라서, 이미지 해상도에 따라 메모리 사용이 빠르게 증가합니다. VAE 타일링은 결과 이미지를 여러 타일로 나눠 디코드하고 다시 합치는 방식입니다.

WebUI에서 VAE 타일링 켜기

WebUI 계열에서는 보통 설정(또는 확장)에서 VAE tiling 옵션을 제공합니다. 버전에 따라 위치가 다르지만, 핵심은 “VAE encode/decode에 타일링 적용”입니다.

  • 효과: VAE 디코드 단계 VRAM 피크 감소
  • 비용: 타일 경계 처리로 약간의 속도 저하(환경에 따라 체감 다름)

diffusers에서 VAE 타일링 켜기

diffusers는 VAE 타일링을 메서드로 제공합니다.

import torch
from diffusers import StableDiffusionPipeline

pipe = StableDiffusionPipeline.from_pretrained(
    "runwayml/stable-diffusion-xl-base-1.0",
    torch_dtype=torch.float16,
).to("cuda")

# VAE 디코드 타일링
pipe.enable_vae_tiling()

# 필요 시 VAE 슬라이싱도 함께
pipe.enable_vae_slicing()

image = pipe(
    prompt="a cinematic photo of a city at night",
    height=1024,
    width=1024,
    num_inference_steps=30,
).images[0]
  • enable_vae_tiling()은 큰 폭의 피크 감소에 유리
  • enable_vae_slicing()은 배치/채널 방향으로 나눠 처리하는 방식으로, 상황에 따라 추가 절감

둘을 같이 켜도 되지만, 속도/품질/호환성을 보고 조합을 결정하세요.

xFormers와 VAE 타일링의 “적용 우선순위”

현장에서 빠르게 OOM을 줄이려면 아래 순서가 시행착오가 적습니다.

  1. xFormers 활성화 (대부분의 케이스에서 이득)
  2. 해상도/배치 크기/스텝을 현실적인 범위로 조정
  3. OOM이 끝에서 난다면 VAE 타일링 활성화
  4. 여전히 부족하면 오프로딩/정밀도/체크포인트 최적화로 확장

특히 OOM은 “평균”이 아니라 “피크”가 문제라서, xFormers와 VAE 타일링처럼 피크를 깎는 도구가 체감 효과가 큽니다.

추가로 VRAM을 아끼는 실전 옵션들

xFormers와 VAE 타일링만으로도 해결되는 경우가 많지만, 아래 옵션을 같이 쓰면 생존 범위가 넓어집니다.

1) 배치 크기와 해상도부터 의심

  • 배치 2에서 OOM이면 배치 1
  • 1024x1024에서 OOM이면 896x896 또는 768x768로 내려서 확인

해상도는 VRAM에 “제곱 느낌”으로 영향을 줍니다. 같은 모델이어도 768에서 되던 것이 1024에서 갑자기 터지는 게 정상입니다.

2) Half precision(fp16)와 VAE 정밀도 조합

일반적으로 fp16은 VRAM을 줄이지만, VAE에서 밴딩/색 깨짐 또는 NaN 문제가 생기는 환경이 있습니다. 그때는 VAE만 fp32로 돌리는 옵션이 필요할 수 있습니다(도구마다 플래그가 다름).

다만 VAE를 fp32로 올리면 VRAM이 늘 수도 있으니, 이 경우는 품질/안정성 우선일 때만 선택하세요.

3) 메모리 정리 루틴(특히 반복 실행 시)

노트북/서버에서 여러 번 생성 루프를 돌리면 PyTorch 캐시 때문에 “한 번은 되는데 두 번째에 터지는” 현상이 나올 수 있습니다.

import torch
import gc

def cleanup():
    gc.collect()
    torch.cuda.empty_cache()

# 생성 후
cleanup()

이건 근본 해결은 아니지만, 워크로드에 따라 피크를 조금 더 안정적으로 관리하는 데 도움이 됩니다.

4) 체크포인트/모델 선택

SDXL은 기본적으로 SD 1.5보다 무겁습니다. 같은 VRAM이면 SDXL에서 더 자주 OOM이 납니다. 목표가 “무조건 고해상도”가 아니라면, 더 가벼운 모델/체크포인트로 요구사항을 맞추는 것도 최적화의 일부입니다.

트러블슈팅: 켰는데도 OOM이면 어디를 보나

케이스 A: xFormers 켰는데 속도만 느려짐

  • 일부 GPU/드라이버 조합에서는 xFormers 커널이 최적 경로를 못 타서 느려질 수 있습니다.
  • 이때는 SDPA 경로(도구에서 제공하는 sdp 옵션 등)나 PyTorch 업그레이드가 더 나을 수 있습니다.

케이스 B: VAE 타일링 켰더니 경계 아티팩트가 보임

  • 타일 경계에서 미세한 이음새가 보일 수 있습니다(환경/설정/후처리에 따라 다름).
  • 해결책은 타일 크기 조정, 후처리(약한 디노이즈/블러), 또는 VAE 슬라이싱으로 대체입니다.

케이스 C: “마지막 1스텝”에서만 계속 터짐

  • 이 패턴은 VAE 디코드가 범인일 확률이 큽니다.
  • VAE 타일링 + 출력 해상도 소폭 하향을 같이 적용하면 해결되는 경우가 많습니다.

추천 조합(빠른 처방전)

아래는 “일단 살아남고 보자”는 관점의 조합입니다.

8GB VRAM

  • xFormers: 켬
  • 해상도: 512 또는 768 중심
  • SDXL은 타협 필요(스텝/해상도/후처리 조정)
  • VAE 타일링: 고해상도 출력 시 켬

12GB VRAM

  • xFormers: 켬
  • 768 안정권, 1024는 설정에 따라 아슬아슬
  • OOM이 끝에서 나면 VAE 타일링 추가

24GB VRAM

  • 대부분의 일반 워크로드는 여유
  • 다만 배치/고해상도/ControlNet 다중 적용/비디오 계열로 가면 여전히 피크가 튈 수 있어 xFormers는 여전히 유효

마무리: OOM은 “피크 관리” 문제다

Stable Diffusion의 VRAM OOM은 단순히 “메모리가 부족해서”라기보다, 특정 구간에서 순간 피크가 치솟는 구조 때문에 발생합니다. 그래서 가장 효율적인 대응은

  • U-Net Attention 피크를 줄이는 xFormers
  • VAE 디코드 피크를 줄이는 VAE 타일링

처럼, 원인 지점에 맞는 도구를 적용하는 것입니다.

다음에 OOM이 나면 “어느 단계에서 터졌는지”부터 확인하고, 그 단계의 피크를 깎는 옵션을 우선 적용해 보세요. 이 접근이 가장 빠르고, 재현 가능하며, 설정 지옥을 줄여줍니다.