Published on

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

Authors
Binance registration banner

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 가속 페달”을 조심스럽게 밟으면서 자신만의 안정 구간을 찾는 게 가장 효율적입니다.