Published on

SDXL VRAM OOM 해결 9가지 실전 체크리스트

Authors

SDXL(Stable Diffusion XL)은 기본적으로 UNet이 크고(파라미터 규모), 해상도도 1024x1024를 기본 타깃으로 잡는 경우가 많아 VRAM 사용량이 급격히 치솟습니다. 그래서 CUDA out of memory 또는 RuntimeError: CUDA error: out of memory 같은 OOM이 흔하게 발생합니다.

이 글은 “무조건 배치 줄이세요” 같은 뻔한 팁을 넘어서, 어떤 설정이 VRAM을 얼마나 건드리는지를 기준으로 9가지를 우선순위대로 정리합니다. WebUI(AUTOMATIC1111), ComfyUI, Diffusers(Python) 어디에서든 통하는 원리 위주로 설명합니다.

참고: OOM은 단순히 VRAM이 부족한 것뿐 아니라, 메모리 단편화, 피크 메모리 급등, 백그라운드 프로세스 점유가 겹쳐서 발생하는 경우가 많습니다. 따라서 한 가지 처방이 아니라 “피크를 깎는 조합”이 정답인 경우가 많습니다.

관련해서 장애를 빠르게 진단하는 접근은 쿠버네티스의 크래시 분석과도 비슷합니다. 원인 분류 후 체크리스트로 좁혀가는 방식이 도움이 됩니다: Kubernetes CrashLoopBackOff 10가지 원인과 15분 진단

1) 해상도와 픽셀 수를 먼저 줄여 피크를 깎기

SDXL에서 VRAM을 가장 크게 좌우하는 건 **해상도(정확히는 latent 공간의 토큰 수)**입니다. 1024x1024에서 1152x896, 1216x832처럼 조금만 올려도 VRAM 피크가 급증합니다.

  • 가장 확실한 처방: 1024x1024896x896 또는 832x1216 같은 “비슷한 픽셀 수”로 낮추기
  • 업스케일은 **후처리(Hi-Res fix 또는 별도 업스케일러)**로 분리하기

WebUI라면 우선 Width/Height를 낮춰 1장이라도 안정적으로 생성되는 기준점을 만든 뒤, 다른 최적화를 얹는 게 좋습니다.

2) Batch size와 Batch count를 분리해서 생각하기

OOM은 대부분 동시에 올리는 텐서 수 때문에 납니다.

  • Batch size는 VRAM에 직접적으로 큰 영향을 줍니다(동시 샘플 수 증가)
  • Batch count는 순차 생성이라 VRAM 영향이 상대적으로 작습니다

따라서 “여러 장이 필요”하면 Batch size=1을 유지하고 Batch count를 늘리는 방향이 안전합니다.

3) SDXL에서 Hi-Res fix는 “2차 생성”이라 VRAM 폭탄이 될 수 있음

Hi-Res fix는 보통

  1. 저해상도에서 1차 생성
  2. 업스케일 후 2차로 다시 디노이즈

를 수행합니다. 이 2차 단계가 고해상도 latent + UNet 조합으로 피크가 터지기 쉽습니다.

권장 전략:

  • OOM이 나면 Hi-Res fix를 끄고 1차 생성만 성공시키기
  • 꼭 필요하면 Denoising strength를 낮추고(예: 0.35 근처), 업스케일 배율을 줄이기(예: 1.5x)
  • 업스케일러를 별도 파이프라인(ESRGAN, 4x-UltraSharp 등)으로 분리

4) Attention 최적화: xFormers 또는 SDP로 메모리 사용량 줄이기

SDXL은 attention 메모리 비중이 큽니다. 따라서 attention 구현을 바꾸는 것만으로도 OOM이 해결되는 경우가 많습니다.

WebUI(AUTOMATIC1111)

  • --xformers 옵션 사용(환경에 따라 설치 필요)
  • 또는 PyTorch의 SDP(Scaled Dot-Product Attention) 최적화를 활용

Diffusers(Python) 예시

아래는 Diffusers에서 메모리 효율 attention을 켜는 대표 패턴입니다.

import torch
from diffusers import StableDiffusionXLPipeline

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

# 1) xFormers (설치되어 있을 때)
pipe.enable_xformers_memory_efficient_attention()

# 2) 또는 attention slicing
pipe.enable_attention_slicing("auto")

image = pipe(
    prompt="cinematic photo, 35mm",
    width=1024,
    height=1024,
    num_inference_steps=30,
).images[0]

주의할 점:

  • xFormers는 드라이버, CUDA, PyTorch 버전에 따라 설치 난이도가 있습니다.
  • 성능과 품질은 대체로 유지되지만, 일부 환경에서는 미세한 차이가 날 수 있습니다.

5) VAE가 의외의 복병: VAE를 FP16으로, 필요 시 타일링 사용

OOM이 “거의 다 됐는데 마지막에 터지는” 패턴이라면 VAE 디코딩이 트리거인 경우가 있습니다. 특히 고해상도 디코딩에서 VRAM이 순간적으로 튀는 일이 있습니다.

해결책:

  • VAE를 fp16으로 운용
  • VAE tiling(타일 단위 디코딩) 지원 시 활성화

Diffusers 예시:

import torch
from diffusers import StableDiffusionXLPipeline

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

pipe.vae.enable_tiling()  # VRAM 절약, 약간 느려질 수 있음

img = pipe("a portrait photo", width=1024, height=1024).images[0]

6) Gradient checkpointing은 학습용, 추론에는 다른 레버가 더 큼

가끔 “체크포인팅 켜면 VRAM 줄죠?”라는 질문이 있는데, **학습(training)**에서는 강력하지만 **추론(inference)**에서는 체감이 제한적입니다.

추론에서 더 효과적인 순서는 보통 이렇습니다.

  1. 해상도 / 배치
  2. attention 최적화(xFormers/SDP)
  3. fp16/bf16
  4. VAE 타일링
  5. CPU offload

즉, 추론 OOM이면 체크포인팅보다 offload나 attention 최적화부터 보세요.

7) CPU offload / sequential offload로 “VRAM 상주 모델”을 줄이기

VRAM이 작은 GPU(예: 8GB, 6GB)에서 SDXL을 돌릴 때는 오프로딩이 현실적인 해법입니다. 속도는 느려지지만 “돌아가게” 만들 수 있습니다.

Diffusers 예시:

import torch
from diffusers import StableDiffusionXLPipeline

pipe = StableDiffusionXLPipeline.from_pretrained(
    "stabilityai/stable-diffusion-xl-base-1.0",
    torch_dtype=torch.float16,
)

# VRAM이 매우 빡빡할 때
pipe.enable_model_cpu_offload()

img = pipe("ultra detailed", width=1024, height=1024, num_inference_steps=25).images[0]

ComfyUI도 노드 그래프 구성에 따라 VRAM 상주량이 달라집니다. 동일한 결과를 내더라도

  • refiner를 항상 붙여 두는 그래프
  • 필요할 때만 로드/실행하는 그래프

는 VRAM 피크가 달라질 수 있습니다.

8) SDXL Refiner는 ‘옵션’이 아니라 ‘두 번째 파이프라인’이다

SDXL은 base와 refiner를 함께 쓰면 품질이 좋아지지만, 구조적으로는 큰 모델을 두 개 운용하는 셈입니다.

OOM이 난다면 우선순위는 다음입니다.

  • refiner를 끄고 base만으로 안정화
  • refiner를 쓰더라도 refiner switch 비율을 조정해 refiner 구간을 줄이기
  • 혹은 refiner를 별도 실행(이미지 저장 후 refiner-only 파이프라인)

특히 VRAM 8GB 이하에서는 base+refiner 동시 운용이 사실상 불가능한 조합이 자주 나옵니다.

9) 메모리 단편화와 백그라운드 점유: “남아 있는 VRAM”을 의심하기

nvidia-smi를 보면 VRAM이 조금 남아 있는데도 OOM이 나는 경우가 있습니다. 이때는

  • PyTorch CUDA allocator 단편화
  • 다른 프로세스(브라우저, 게임 런처, 녹화툴)의 VRAM 점유
  • WebUI 확장(extensions) 또는 미리보기 기능이 만든 추가 텐서

가 원인일 수 있습니다.

즉시 점검 체크리스트

  • nvidia-smi로 불필요 프로세스 종료
  • WebUI라면 확장 비활성화 후 재시도(특히 ControlNet, 애니메이션 계열)
  • 세션을 오래 켜뒀다면 프로세스 재시작(누수/캐시로 피크가 커질 수 있음)

PyTorch 쪽에서는 환경 변수로 allocator 동작을 조정하기도 합니다(환경/버전에 따라 효과 차이).

# 예: 단편화 완화에 도움이 될 수 있는 설정(환경에 따라 다름)
export PYTORCH_CUDA_ALLOC_CONF="max_split_size_mb:128"

운영 환경에서 “원인 추적 후 빠르게 차단”하는 방식은 프론트 성능 병목을 추적하는 접근과도 비슷합니다. 한 번에 감으로 고치기보다, 지표를 보고 피크를 줄이는 쪽이 성공률이 높습니다: Chrome LCP 느림? Render‑Blocking 리소스 추적법

실전 권장 적용 순서(가장 덜 아픈 것부터)

  1. Batch size=1 고정, 해상도 1024x1024 이하로 기준점 만들기
  2. refiner 끄기(또는 switch 구간 축소)
  3. Hi-Res fix 끄고 1차 생성만 성공시키기
  4. xFormers 또는 SDP 계열 attention 최적화
  5. VAE fp16 및 VAE tiling
  6. CPU offload로 “돌아가게” 만들기
  7. 확장/백그라운드 프로세스 정리, 프로세스 재시작

마무리: OOM은 ‘총량’보다 ‘피크’ 문제다

SDXL OOM은 단순히 “VRAM이 부족해서”가 아니라, 특정 단계(attention, VAE 디코딩, Hi-Res 2차 디노이즈, refiner 전환)에서 순간 피크가 튀면서 발생하는 경우가 많습니다. 그래서 해결도 “한 방 옵션”보다 피크를 만드는 기능을 분리하고, 메모리 효율 attention과 오프로딩을 조합하는 쪽이 재현성과 성공률이 높습니다.

다음에 OOM이 났을 때는 에러 로그를 보고 “어느 단계에서 터졌는지”부터 잡아보세요. 그 다음 이 글의 9가지 중 해당되는 레버를 순서대로 적용하면, 같은 GPU에서도 훨씬 안정적으로 SDXL을 굴릴 수 있습니다.