Published on

SDXL VRAM 폭발 해결 - VAE·Tiling 실전 가이드

Authors

SDXL로 고해상도 이미지를 뽑다 보면 갑자기 VRAM이 치솟으며 CUDA out of memory 로 작업이 터지는 순간이 옵니다. 특히 1024x1024 이상, Hires fix, 업스케일, 디테일 강화(ADetailer 류), 배치 증가를 한 번에 얹으면 “잘 되다가도” 특정 단계에서 폭발합니다.

이 글은 SDXL에서 VRAM이 어디서 급증하는지(특히 VAE 디코드 구간), 그리고 VAE 선택/설정Tiling(타일링) 전략으로 OOM을 실전에서 끊는 방법을 정리합니다. 목표는 “품질을 크게 잃지 않으면서” 안정적으로 돌리는 것입니다.

참고로 운영 중 장애 대응처럼, 재시도/백오프를 설계하듯(리소스가 부족할 때 우아하게 degrade) 그래픽 메모리도 단계별로 안전장치를 두는 접근이 유효합니다. 관련 사고방식은 Claude API 529 Overloaded 재시도·백오프 설계 글의 패턴과도 닮아 있습니다.

SDXL에서 VRAM이 폭발하는 대표 지점

SDXL 파이프라인을 크게 나누면 다음과 같습니다.

  1. UNet(denoising) 단계: 샘플링 스텝 동안 반복 계산. 해상도·배치·CFG·컨트롤넷·LoRA 수에 따라 꾸준히 VRAM을 먹습니다.
  2. VAE 디코드 단계: latent를 RGB로 복원하는 순간. 여기서 “마지막에 갑자기” VRAM이 튀는 케이스가 많습니다.
  3. 후처리(업스케일/리파이너/디테일러): 추가 모델을 얹는 순간 피크가 겹치며 터집니다.

특히 SDXL은 기본 해상도가 높고, latent 크기도 커서 VAE 디코드가 의외로 병목이 됩니다. 그래서 “샘플링은 끝났는데 저장하는 순간 죽는” 현상이 자주 나옵니다.

먼저 확인할 것: OOM 패턴을 로그로 분류하기

OOM이 언제 나는지에 따라 처방이 달라집니다.

  • 샘플링 중간에 OOM: 해상도/배치/컨트롤넷/LoRA 과다, xFormers/SDP 설정, UNet 메모리 최적화가 우선
  • 샘플링 끝나고 디코드/저장 시 OOM: VAE 디코드가 원인일 가능성 큼 → 타일드 VAE/타일링이 효과적
  • Hires fix 2차 패스에서 OOM: 업스케일 배율·2차 해상도·디노이즈 강도·업스케일러가 원인 → 2차 패스 타일링 또는 파라미터 절제

ComfyUI라면 큐 실행 중 어느 노드에서 터지는지, WebUI라면 콘솔에서 “VAE decode” 타이밍인지 확인하세요.

VAE가 VRAM을 잡아먹는 이유

VAE는 latent 공간(압축 표현)을 픽셀 공간(RGB)으로 복원합니다. 이 과정은 대개 고해상도 텐서를 다루며, 다음 요인들이 VRAM 피크를 키웁니다.

  • 디코드 시 한 번에 전체 이미지를 펼침: 큰 텐서가 한 번에 생성
  • 정밀도(fp16/bf16/fp32): VAE가 fp32로 돌면 메모리 사용량이 급증
  • 배치 처리: 배치가 N 이면 디코드 텐서도 N

따라서 VAE 쪽에서는 다음 두 축이 핵심입니다.

  1. VAE 정밀도/구현 선택
  2. 타일드 디코드(타일링)로 피크를 낮추기

해결 1: SDXL 전용 VAE로 교체하고, VAE 정밀도를 고정

SDXL은 SD 1.5 계열 VAE와 호환이 “완전히” 같지 않습니다. 잘못된 VAE를 쓰면 색감/디테일 문제가 생길 뿐 아니라, 내부적으로 비효율적인 경로를 타는 구성도 나올 수 있습니다.

WebUI(A1111) 기준 체크

  • SDXL 모델에 맞는 VAE를 선택하거나, 모델 번들 VAE를 사용
  • Settings 에서 VAE 관련 옵션을 점검

일반적으로 권장되는 방향은 다음입니다.

  • 가능하면 fp16 VAE 사용
  • VAE in full precision 류 옵션이 켜져 있다면 끄기(환경에 따라 명칭 상이)

정확한 옵션명은 버전/확장에 따라 다르지만, 핵심은 “VAE만 fp32로 강제되지 않게” 하는 것입니다.

ComfyUI 기준 체크

ComfyUI는 노드 그래프에서 VAE를 명시적으로 로드하는 경우가 많습니다.

  • VAELoader 에서 SDXL용 VAE를 선택
  • VAE Decode 단계가 OOM이면 타일드 디코드 노드로 교체 고려

해결 2: 타일링의 2종류를 구분하자

SDXL에서 흔히 말하는 타일링은 사실 두 가지가 섞여 있습니다.

1) Latent/샘플링 타일링(UNet 타일링)

  • 샘플링 자체를 타일 단위로 수행
  • 장점: UNet 단계 VRAM을 크게 줄임
  • 단점: 타일 경계 이음새(seam) 관리가 필요

2) VAE 타일링(타일드 디코드)

  • 샘플링은 그대로 하고, VAE 디코드만 타일로 쪼갬
  • 장점: 품질 손실이 상대적으로 적고, “마지막에 터지는 OOM”에 직빵
  • 단점: 디코드 시간이 늘어날 수 있음

이번 글의 핵심은 VAE·Tiling 이므로, 우선순위는 보통 다음과 같습니다.

  1. 디코드 OOM이면 VAE 타일링부터
  2. 샘플링 OOM이면 UNet 타일링 또는 해상도/배치 조절

해결 3: VAE 타일드 디코드 적용(가장 체감 큼)

WebUI에서의 접근

WebUI는 확장이나 설정으로 “타일드 VAE” 기능을 제공하는 경우가 많습니다. 개념적으로는 다음과 같은 파라미터가 존재합니다.

  • tile_size: 한 번에 디코드할 타일 크기
  • overlap: 타일 경계 블렌딩용 겹침 영역

권장 시작값(경험칙):

  • tile_size256 또는 512 로 시작
  • seam이 보이면 overlap32~64 로 올림

VRAM이 아주 빡빡하면 tile_size 를 더 낮추되(예: 128), 속도 저하와 seam 가능성이 올라갑니다.

ComfyUI에서의 접근

ComfyUI는 보통 VAE Decode 를 타일드 디코드 노드로 바꾸는 방식이 명확합니다.

  • VAE Decode (Tiled) 류 노드 사용
  • tile_size, overlap 조정

핵심은 “OOM이 VAE 디코드에서 난다”가 확실할 때, 샘플링 파라미터를 건드리기 전에 디코드만 분할하는 것입니다.

해결 4: Hires fix/업스케일 단계에 타일링을 걸어라

SDXL에서 VRAM 폭발이 가장 자주 나는 조합은 다음입니다.

  • 1차: 1024x1024 생성
  • 2차: Hires fix로 1536~2048 이상 업스케일 + 디테일 강화
  • 거기에 ADetailer/Refiner까지 추가

이때는 “1차는 되는데 2차에서 터지는” 전형적인 패턴이 나옵니다.

대응은 두 갈래입니다.

  • 2차 패스 해상도를 낮추거나 배율을 줄이기
  • 2차 패스의 디코드/업스케일에 타일링 적용

특히 업스케일러(ESRGAN/4x 계열)도 타일 옵션이 있는 경우가 많고, 이 타일은 픽셀 공간 업스케일 타일링이라 VAE 타일링과는 별개로 도움이 됩니다.

해결 5: 배치와 동시 기능을 “겹치지 않게” 스케줄링

VRAM 피크는 대개 “동시에” 여러 큰 텐서가 올라올 때 생깁니다. 예를 들면:

  • 배치 2 이상
  • 컨트롤넷 2
  • LoRA 여러 개
  • Refiner 동시 사용

이것들을 한 번에 올리면 피크가 겹칩니다. 운영 관점에서는 동시성 제한(concurrency limit)과 같습니다.

실전 팁:

  • 배치가 필요하면 batch_size 대신 batch_count 로 시간과 VRAM을 교환
  • Refiner는 꼭 필요할 때만 켜고, 먼저 base만 안정화
  • 컨트롤넷/어댑터는 하나씩 추가하며 OOM 지점을 찾기

코드 예제: diffusers에서 VAE 타일링으로 OOM 줄이기

아래는 Python diffusers 기반에서 SDXL 파이프라인에 VAE 타일링/슬라이싱을 켜는 대표 패턴입니다. (버전에 따라 메서드 존재 여부가 다를 수 있으니, 없으면 최신 diffusers로 업데이트하세요.)

import torch
from diffusers import StableDiffusionXLPipeline

model_id = "stabilityai/stable-diffusion-xl-base-1.0"

pipe = StableDiffusionXLPipeline.from_pretrained(
    model_id,
    torch_dtype=torch.float16,
    variant="fp16",
).to("cuda")

# 1) VAE가 디코드에서 VRAM 피크를 만들 때: 타일링/슬라이싱
# 타일링은 큰 이미지를 타일로 나눠 디코드해 피크 VRAM을 낮춥니다.
pipe.vae.enable_tiling()

# 슬라이싱은 배치/채널 방향 분할로 추가 절감(환경에 따라 체감 다름)
pipe.vae.enable_slicing()

# 2) 메모리 효율 어텐션(가능하면)
# pipe.enable_xformers_memory_efficient_attention()

prompt = "ultra detailed photo, cinematic lighting"
image = pipe(
    prompt=prompt,
    width=1024,
    height=1024,
    num_inference_steps=30,
    guidance_scale=5.5,
).images[0]

image.save("out.png")

추가로, UNet 단계에서 터진다면 enable_xformers_memory_efficient_attention() 또는 PyTorch의 SDP 설정, 해상도/배치 조절이 더 직접적입니다. 하지만 “마지막 저장에서 죽는” 타입은 위 VAE 타일링이 가장 먼저 먹힙니다.

코드 예제: ComfyUI 스타일로 생각하는 타일드 디코드 의사코드

ComfyUI는 노드 기반이지만, 로직은 아래처럼 이해하면 디버깅이 쉬워집니다.

latent = ksampler(model=sdxl, latent=empty_latent, steps=30, cfg=5.5)

# 여기서 OOM이 나면 UNet 최적화/해상도/배치/컨트롤넷을 줄여야 함

# 여기서 OOM이 나면 VAE 타일드 디코드가 정답
image = vae_decode_tiled(vae=sdxl_vae, latent=latent, tile_size=512, overlap=64)

save(image)

즉, OOM 위치가 ksampler 쪽인지 vae_decode 쪽인지로 처방이 갈립니다.

품질 이슈: 타일링 seam(경계선) 줄이는 법

타일링을 적용하면 가장 신경 쓰이는 게 경계선입니다. 다음 순서로 접근하면 시행착오가 줄어듭니다.

  1. overlap 을 늘린다: 326496
  2. tile_size 를 너무 작게 하지 않는다: 가능하면 256 이상 유지
  3. 업스케일 타일링과 VAE 타일링을 동시에 과도하게 낮추지 않는다

seam이 계속 보이면, VAE 타일링은 유지하되 업스케일 타일 옵션(픽셀 업스케일 타일)을 조정하거나, 2차 패스 해상도를 낮추는 편이 결과가 더 깔끔할 때가 많습니다.

VRAM 절감 체크리스트(우선순위)

아래는 SDXL에서 “VRAM 폭발을 끊는” 우선순위 체크리스트입니다.

  1. OOM 지점이 디코드/저장인지 확인
  2. SDXL용 VAE 사용 + VAE fp16 경로 확인
  3. VAE 타일드 디코드 적용(tile_size=512, overlap=64부터)
  4. 배치는 batch_size 대신 batch_count 로 전환
  5. Hires fix 2차 해상도/배율을 보수적으로
  6. 업스케일러 타일 옵션(픽셀 타일)도 병행
  7. 컨트롤넷/LoRA/Refiner는 하나씩 추가하며 피크를 관찰

이 과정은 성능 튜닝에서 “병목을 먼저 찾고, 그 병목만 수술”하는 것과 같습니다. 무작정 옵션을 다 끄면 품질이 떨어지니, OOM이 나는 단계에만 타겟팅하는 게 핵심입니다.

자주 묻는 조합별 처방

1024x1024 는 되는데 1536 이상에서 저장 시 OOM

  • VAE 타일드 디코드 적용
  • tile_size512 또는 256 으로 낮춤
  • 배치 1 유지

Hires fix 켜면 2차 패스에서 OOM

  • 2차 해상도 목표를 낮추기(예: 20481536)
  • 업스케일러 타일 옵션 켜기
  • 2차 패스도 VAE 타일드 디코드 유지

컨트롤넷 여러 개에서 샘플링 중 OOM

  • 컨트롤넷 수/해상도 축소
  • UNet 메모리 효율 어텐션(xFormers/SDP) 적용
  • 필요 시 latent 타일링 고려

마무리

SDXL의 VRAM 폭발은 “모델이 무겁다”보다, 특정 단계에서 피크가 겹치는 구조 때문에 발생하는 경우가 많습니다. 그리고 그 피크의 단골 범인이 VAE 디코드입니다.

정리하면,

  • 샘플링은 되는데 마지막에 죽는다 → VAE 타일드 디코드가 1순위
  • 샘플링 중간에 죽는다 → UNet 쪽 최적화/해상도/배치/동시 기능 축소
  • Hires fix에서 죽는다 → 2차 패스 해상도와 업스케일 타일링을 함께 조정

위 체크리스트대로 “OOM이 나는 지점”을 먼저 분류하고, VAE와 타일링을 정확히 적용하면, 같은 GPU에서도 SDXL을 훨씬 안정적으로 굴릴 수 있습니다.