Published on

Stable Diffusion VRAM 부족? xFormers·VAE 타일링

Authors

Stable Diffusion을 로컬 GPU에서 돌리다 보면 가장 흔히 마주치는 문제가 VRAM 부족입니다. 특히 1024x1024 같은 고해상도, 배치 증가, 고스텝, Hires.fix, ControlNet/LoRA 동시 사용을 얹는 순간 CUDA out of memory가 터지기 쉽습니다.

이 글은 “무작정 해상도 낮추기” 대신, 어텐션 메모리를 줄이는 xFormersVAE 디코딩을 쪼개는 VAE 타일링을 중심으로 VRAM 사용량을 안정적으로 낮추는 방법을 다룹니다. 마지막에는 OOM이 계속 날 때 점검 순서까지 제공합니다.

관련해서 시스템이 메모리 압박을 받을 때 원인을 추적하는 관점은 리눅스 OOM 글도 도움이 됩니다: 리눅스 OOM Killer로 프로세스 죽을 때 원인 추적

VRAM은 어디서 터질까: SD 메모리 지형

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

  1. U-Net의 어텐션(Attention) 연산
    • 해상도가 커질수록 토큰 수가 늘고, 어텐션의 중간 텐서가 폭증합니다.
  2. VAE 디코딩(라티언트 4채널 -> RGB 이미지)
    • 최종 단계에서 한 번 크게 메모리를 씁니다. 고해상도에서 특히 치명적입니다.
  3. 추가 모듈(ControlNet, IP-Adapter, 여러 LoRA, Refiner 등)
    • 모델 파라미터와 중간 활성화가 증가합니다.
  4. 배치 크기, CFG, 샘플러 설정
    • 배치가 늘면 거의 선형으로 VRAM이 증가합니다.

따라서 VRAM 최적화는 보통 두 갈래로 나뉩니다.

  • 어텐션 메모리를 줄인다: xFormers / SDPA / 메모리 효율 어텐션
  • VAE 디코딩 메모리를 줄인다: VAE 타일링(tiling)

xFormers: “어텐션” 메모리를 깎는 가장 큰 레버

xFormers는 메모리 효율적인 어텐션 커널을 제공해, U-Net 어텐션에서 발생하는 중간 텐서 메모리를 크게 줄여줍니다. 체감상 “해상도를 한 단계 올릴 수 있게 되는” 수준의 효과가 나오는 경우가 많습니다.

어떤 환경에서 효과가 큰가

  • 768x768, 1024x1024 같은 고해상도
  • Hires.fix로 2-pass를 돌리는 경우
  • ControlNet을 여러 개 붙이는 경우

반대로 아주 낮은 해상도에서는 속도/메모리 차이가 작을 수 있습니다.

AUTOMATIC1111에서 xFormers 켜기

가장 흔한 WebUI 기준으로는 실행 옵션에 --xformers를 넣습니다.

# webui-user.sh 또는 webui-user.bat의 COMMANDLINE_ARGS 예시
COMMANDLINE_ARGS="--xformers"

실행 후 콘솔 로그에 xFormers가 활성화되었다는 문구가 보이면 정상입니다. 만약 설치가 꼬이면, PyTorch/CUDA 버전과 xFormers 휠이 안 맞는 경우가 많습니다.

ComfyUI에서 xFormers 켜기

ComfyUI는 환경에 따라 다르지만 보통 아래 형태로 활성화합니다.

python main.py --use-xformers

또는 일부 배포판/런처는 설정 메뉴에서 토글을 제공합니다.

xFormers vs PyTorch SDPA

최근 PyTorch는 SDPA(Scaled Dot-Product Attention) 경로가 좋아져서, 환경에 따라 xFormers 없이도 메모리 효율 어텐션이 동작합니다.

  • xFormers: 설치/호환성 이슈가 가끔 있음, 대신 폭넓게 검증된 케이스가 많음
  • SDPA: PyTorch/드라이버 조합에 따라 자동 최적화, 설치 단순

하지만 “당장 VRAM이 터진다”면, WebUI/ComfyUI가 제공하는 방식으로 xFormers를 먼저 시도하는 게 빠른 해결책인 경우가 많습니다.

VAE 타일링: “마지막 디코딩” OOM을 잡는 비장의 카드

xFormers로도 해결이 안 되는 OOM은 의외로 VAE 디코딩 단계에서 터집니다. 특히 1024x1024 이상, 혹은 Hires.fix의 업스케일 패스에서 VAE가 한 번에 큰 텐서를 만들다가 VRAM을 넘기곤 합니다.

VAE 타일링은 VAE 디코딩을 작은 타일로 쪼개서 순차 처리하는 방식입니다.

  • 장점: VRAM 사용량을 크게 낮춤
  • 단점: 속도가 느려질 수 있음, 타일 경계에서 미세한 이음새가 생길 수 있음(대부분 설정으로 완화 가능)

AUTOMATIC1111에서 VAE 타일링

WebUI 확장이나 빌드에 따라 옵션 제공 방식이 다를 수 있지만, 핵심은 “VAE를 타일 단위로 처리”하도록 켜는 것입니다.

일반적으로는 Settings에서 VAE 관련 항목에 Tiling 또는 VAE tiling 토글이 존재합니다. 없다면 확장(Extension)로 제공되는 경우도 있습니다.

diffusers(Python)에서 VAE 타일링 켜기

서버/배치 작업에서 diffusers를 쓴다면 코드로 확실하게 제어할 수 있습니다.

import torch
from diffusers import StableDiffusionPipeline

pipe = StableDiffusionPipeline.from_pretrained(
    "runwayml/stable-diffusion-v1-5",
    torch_dtype=torch.float16,
).to("cuda")

# 어텐션 메모리 최적화(환경에 따라 xFormers 또는 SDPA)
pipe.enable_xformers_memory_efficient_attention()

# VAE 타일링: 디코딩 VRAM 급증 완화
pipe.enable_vae_tiling()

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

image.save("out.png")

enable_vae_tiling()은 특히 “생성은 거의 다 됐는데 마지막에 OOM” 같은 패턴에 강합니다.

VAE 타일링의 품질 이슈(이음새) 줄이기

타일 경계 이슈가 보이면 아래를 순서대로 시도합니다.

  • 타일 크기를 키워 경계 수를 줄이기(가능한 VRAM 범위 내)
  • 디코딩 후 약한 후처리(예: 아주 약한 디노이즈/샤픈 최소화)
  • 업스케일 파이프라인에서는 업스케일러/샘플러 조합 변경

같이 쓰면 더 강력한 저VRAM 옵션들

xFormers와 VAE 타일링만으로도 많은 케이스가 해결되지만, 여전히 빡빡한 GPU(예: 6GB 이하)에서는 추가 옵션이 필요합니다.

1) 해상도 전략: 한 번에 크게 말고, 2-pass로

  • 512 또는 640 정도로 1차 생성
  • Hires.fix 또는 업스케일 + 낮은 denoise로 2차

다만 Hires.fix는 2차 패스에서 VRAM이 더 터질 수 있으니, 그때 VAE 타일링이 특히 유효합니다.

2) 배치 크기와 배치 카운트 구분

  • Batch size는 VRAM에 직접 타격
  • Batch count는 반복 횟수라 VRAM 영향이 훨씬 적음

즉 “여러 장”이 필요하면 Batch size를 올리기보다 Batch count로 해결하는 편이 안전합니다.

3) FP16 / BF16 사용

  • NVIDIA 대부분 환경에서 fp16은 필수 최적화에 가깝습니다.
  • BF16은 GPU 지원 여부에 따라 선택.

4) ControlNet/LoRA 동시 사용 줄이기

ControlNet을 2개, 3개 붙이면 VRAM이 빠르게 올라갑니다. VRAM이 부족하면

  • ControlNet 개수 축소
  • 해상도 낮추고 후처리 업스케일
  • 필요한 LoRA만 남기기

같은 전략이 필요합니다.

OOM이 계속 날 때: 재현 가능한 점검 순서

VRAM 부족은 “진짜 VRAM 부족” 외에도 파편화, 설정 꼬임, 드라이버/라이브러리 조합 문제로 악화될 수 있습니다. 아래 순서로 점검하면 원인 분리가 빠릅니다.

1) OOM이 나는 정확한 시점을 로그로 확인

  • 시작하자마자 OOM: 모델 로딩/추가 모듈 과다
  • 샘플링 중 OOM: 어텐션 메모리(= xFormers 타겟)
  • 마지막 저장 직전 OOM: VAE 디코딩(= VAE 타일링 타겟)

2) 동일 프롬프트로 최소 설정부터 올리기

  • ControlNet/LoRA 끄고
  • 512x512, batch size 1
  • 스텝 20

여기서 안정적이면 하나씩 옵션을 올리며 “어느 옵션이 VRAM을 폭발시키는지” 찾습니다.

3) GPU 메모리 사용량을 수치로 확인

리눅스라면 다음처럼 확인할 수 있습니다.

watch -n 0.5 nvidia-smi

프로세스가 갑자기 죽거나 시스템 메모리까지 압박이 번지면, 커널 OOM 관점의 분석도 도움이 됩니다: 리눅스 OOM Killer 로그로 메모리 누수 추적하기

4) xFormers가 실제로 적용됐는지 확인

실행 옵션을 넣었더라도, 환경에 따라 fallback이 걸려 적용이 안 되는 경우가 있습니다.

  • 콘솔 로그에 xFormers 활성화 메시지 확인
  • PyTorch/CUDA 버전 호환성 확인

5) VAE 타일링을 켰는데도 마지막에 죽으면

  • 저장 포맷(예: PNG) 자체는 VRAM을 크게 쓰지 않지만, 후처리나 업스케일러가 붙어 있을 수 있습니다.
  • 업스케일러를 바꾸거나, 2차 패스 해상도를 조금 낮춰 “임계점” 아래로 내립니다.

실전 추천 조합(8GB 기준)

아래는 “품질과 속도”를 어느 정도 유지하면서 VRAM을 안정화하는 조합입니다.

  • xFormers 활성화
  • fp16 사용
  • batch size 1
  • 768x768 또는 832x832 정도에서 1차 생성
  • 필요 시 Hires.fix를 쓰되, 2차에서 VAE 타일링 활성화
  • ControlNet은 1개부터 시작

6GB 이하라면 VAE 타일링은 사실상 상시 켜는 편이 마음 편한 경우가 많고, 512 기반 2-pass 전략이 더 안전합니다.

마무리: OOM은 “어텐션”과 “VAE”로 나눠서 잡자

Stable Diffusion VRAM 부족은 대개 두 부류로 정리됩니다.

  • 샘플링 중 OOM: 어텐션 메모리 문제일 확률이 높고, xFormers가 1순위 해법
  • 마지막 디코딩/저장 직전 OOM: VAE 디코딩 문제일 확률이 높고, VAE 타일링이 결정타

이 둘을 함께 적용하면, 같은 GPU로도 해상도/파이프라인 여유가 눈에 띄게 늘어납니다. 이후에는 배치, ControlNet, Hires.fix 같은 “VRAM 가속 페달”을 조심스럽게 밟으면서 자신만의 안정 구간을 찾는 게 가장 효율적입니다.