- Published on
Stable Diffusion VRAM 부족 해결 - xFormers·Tiling
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
Stable Diffusion을 돌리다 보면 가장 흔히 마주치는 에러가 CUDA OOM(Out Of Memory)입니다. 특히 SDXL, 고해상도(1024x1024 이상), 배치 증가, ControlNet·LoRA·업스케일러를 동시에 얹는 순간 VRAM이 빠르게 바닥납니다.
이 글은 “성능은 크게 잃지 않으면서 VRAM을 줄이는” 쪽에 초점을 맞춥니다. 핵심은 두 가지입니다.
xFormers기반 메모리 효율 어텐션으로 어텐션 메모리 사용량을 낮춘다.Tiling(타일링)으로 VAE 디코드/인코드 및 Latent 처리를 쪼개서 피크 VRAM을 낮춘다.
추가로, 실제로 OOM을 줄이는 우선순위 체크리스트와, 어떤 설정이 어떤 부작용(속도 저하, 품질 변화)을 만드는지도 함께 정리합니다.
관련해서 모델 학습/LoRA 쪽에서 메모리·품질이 동시에 무너지는 케이스를 다룬 글도 참고가 됩니다: SDXL LoRA 학습이 망가질 때 원인 7가지
VRAM이 어디서 터지는지: 어텐션 vs VAE vs 해상도
OOM을 줄이려면 “피크 VRAM”이 어디서 발생하는지 감을 잡는 게 먼저입니다.
- 어텐션(Attention): U-Net 내부에서 가장 큰 메모리 압박을 만드는 구간입니다. 해상도가 커질수록 토큰 수가 늘고, 어텐션 맵이 커지면서 VRAM이 급증합니다.
- VAE 디코드/인코드: 최종 이미지를 디코드할 때(또는 img2img에서 인코드할 때) 피크가 튀는 경우가 많습니다. 특히 고해상도 출력에서 VAE가 병목이 되기 쉽습니다.
- 배치/스텝/부가 모듈: 배치(
batch size)는 거의 선형으로 VRAM을 잡아먹습니다. ControlNet을 여러 개 켜거나, 고정 시드로 여러 장을 한 번에 뽑는 순간도 위험합니다.
결론적으로, 어텐션 최적화는 xFormers, VAE 피크 완화는 타일링이 가장 직접적입니다.
1) xFormers: 메모리 효율 어텐션으로 U-Net VRAM 줄이기
xFormers는 PyTorch에서 메모리 효율적인 어텐션 커널을 제공해, 동일 조건에서 VRAM 사용량을 줄여줍니다. Stable Diffusion 계열 UI(AUTOMATIC1111, ComfyUI, Invoke 등)에서 옵션으로 많이 노출되어 있습니다.
기대 효과
- VRAM 감소: 특히 고해상도에서 체감이 큽니다.
- 속도: GPU/드라이버/빌드 조합에 따라 빨라지거나 비슷하거나, 드물게 느려질 수 있습니다.
주의점
- 환경에 따라 설치/호환 이슈가 납니다(특히 Windows에서 PyTorch·CUDA 버전이 어긋날 때).
- 특정 최적화 조합에서 드물게 재현성(완전 동일 출력)이 달라질 수 있습니다.
(A) AUTOMATIC1111에서 xFormers 켜기
대부분은 --xformers 플래그로 활성화합니다.
# 예: webui-user.sh 또는 실행 스크립트에 추가
./webui.sh --xformers
Windows라면 webui-user.bat의 COMMANDLINE_ARGS에 넣는 형태가 일반적입니다.
set COMMANDLINE_ARGS=--xformers
실행 로그에 xformers 로드 메시지가 보이면 적용된 것입니다.
(B) ComfyUI에서 xFormers 사용
ComfyUI는 보통 PyTorch/환경에 xformers가 설치되어 있으면 자동으로 사용하거나, 설정에 따라 활성화됩니다. 설치가 필요하다면 환경에 맞춰 pip로 설치합니다.
pip install xformers
다만 이 줄은 “항상 통하는 만능”이 아닙니다. CUDA·PyTorch 버전이 맞지 않으면 빌드가 실패하거나 런타임 오류가 날 수 있습니다. 이 경우는 다음을 우선 점검합니다.
- 현재 PyTorch가 CUDA 빌드인지 확인
- 드라이버가 요구 버전 이상인지 확인
xformers가 현재 PyTorch와 호환되는 wheel을 제공하는지 확인
(C) diffusers(파이썬 코드)에서 xFormers 켜기
직접 diffusers로 파이프라인을 돌리는 경우 아래처럼 활성화합니다.
import torch
from diffusers import StableDiffusionXLPipeline
pipe = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0",
torch_dtype=torch.float16,
variant="fp16",
).to("cuda")
pipe.enable_xformers_memory_efficient_attention()
image = pipe(
prompt="a photo of a cozy desk setup, soft light",
num_inference_steps=30,
guidance_scale=6.5,
).images[0]
image.save("out.png")
여기서도 핵심은 torch_dtype를 float16 또는 bfloat16로 낮춰 VRAM을 먼저 줄이고, 그 다음 xFormers를 얹는 것입니다.
2) Tiling: VAE/Latent 타일링으로 피크 VRAM 낮추기
타일링은 큰 텐서를 한 번에 처리하지 않고, 여러 타일로 쪼개서 순차 처리함으로써 피크 VRAM을 낮추는 기법입니다. Stable Diffusion에서 흔히 말하는 타일링은 보통 두 갈래입니다.
- VAE 타일링: 디코드/인코드를 타일로 쪼갭니다. 고해상도에서 특히 효과가 큽니다.
- Latent 타일링(또는 U-Net 타일링): 확산 과정(denoise)을 타일로 나눕니다. VRAM을 크게 줄일 수 있지만, 경계 이슈나 속도 저하가 더 두드러질 수 있습니다.
언제 타일링이 특히 유효한가
1024x1024이상에서 VAE 디코드 시 OOM이 나는 경우- 업스케일을 크게 걸었을 때 마지막 디코드에서 터지는 경우
- SDXL에서 베이스는 돌아가는데 리파이너나 후처리에서 VRAM이 모자라는 경우
부작용(중요)
- 속도 저하: 타일을 여러 번 처리하므로 시간이 늘어납니다.
- 경계 아티팩트: 타일 경계가 보일 수 있습니다. 보통은 오버랩(겹침)으로 완화합니다.
3) UI별 설정 포인트: “어디서 켜는지”가 다르다
Stable Diffusion 생태계는 UI/워크플로가 다양해서, 같은 기능도 위치가 다릅니다. 아래는 실무에서 많이 쓰는 포인트를 “찾기 쉬운 키워드” 중심으로 정리합니다.
AUTOMATIC1111
xFormers: 실행 옵션--xformersVAE tiling: 확장 기능이나 설정(버전에 따라 다름)에서Tiled VAE또는VAE tiling키워드로 제공되는 경우가 많습니다.Medvram/Lowvram:--medvram,--lowvram는 VRAM을 더 줄이지만 속도 손해가 커서 최후의 수단에 가깝습니다.
ComfyUI
VAE Decode (Tiled)같은 노드가 별도로 존재하거나, 커스텀 노드로 제공되는 경우가 많습니다.- 워크플로에서 “VAE 디코드 단계만 타일링”하는 식으로 부분 적용이 쉬운 편입니다.
diffusers
diffusers는 버전에 따라 지원 기능이 다르지만, 일반적으로는 다음 축으로 접근합니다.
- 어텐션 최적화:
enable_xformers_memory_efficient_attention() - VAE 메모리 완화: 해상도/배치 조절, VAE를 CPU로 오프로딩, 또는 타일 디코드 유틸 사용
아래는 “VAE 디코드를 타일로 쪼개는” 개념을 단순화한 예시(의사 코드에 가깝습니다)입니다.
# 개념 예시: 큰 latent를 타일로 잘라 VAE decode를 순차 처리
# 실제 구현은 사용하는 VAE/라이브러리에 맞춰 조정 필요
def tiled_vae_decode(vae, latents, tile_size=64, overlap=8):
# latents: (B, C, H, W)
b, c, h, w = latents.shape
out = torch.zeros((b, 3, h * 8, w * 8), device=latents.device, dtype=torch.float16)
step = tile_size - overlap
for y in range(0, h, step):
for x in range(0, w, step):
y2 = min(y + tile_size, h)
x2 = min(x + tile_size, w)
tile = latents[:, :, y:y2, x:x2]
# VAE decode
img_tile = vae.decode(tile).sample # 라이브러리에 따라 반환이 다름
# 타일을 합치는 로직(오버랩 블렌딩 등)은 별도 구현 필요
out[:, :, y*8:y2*8, x*8:x2*8] = img_tile
return out
핵심은 “한 번에 디코드하지 않고, 작은 조각으로 나눠서 피크 VRAM을 낮춘다”는 점입니다. 실제로는 오버랩 영역을 가중치로 블렌딩해 경계선을 줄이는 처리가 들어갑니다.
4) VRAM 부족을 줄이는 실전 우선순위(체크리스트)
아래 순서대로 적용하면, 품질 손실을 최소화하면서 성공 확률을 빠르게 올릴 수 있습니다.
1순위: 배치와 해상도부터 조정
batch size를1로 내립니다.- 해상도를 한 단계 낮추고, 필요하면 업스케일로 보완합니다.
- SDXL은
1024x1024가 기본이지만, VRAM이 빡빡하면832x1216같은 타협 해상도를 고려합니다.
2순위: FP16/BF16 사용
- 가능한 경우
float16(또는 Ampere 이후면bfloat16)로 내립니다. - VRAM 감소 효과가 크고, 품질 영향은 보통 미미합니다.
3순위: xFormers 활성화
- 어텐션 메모리 피크를 줄이는 데 가장 “가성비”가 좋습니다.
- 특히 고해상도에서 효과가 잘 납니다.
4순위: VAE 타일링
- “마지막 저장/디코드에서만 터지는” 케이스에 특효입니다.
- 속도는 느려지지만 성공률이 크게 올라갑니다.
5순위: 오프로딩·스왑·저VRAM 모드
- CPU 오프로딩,
--medvram,--lowvram는 최후의 수단으로 두는 편이 좋습니다. - 속도 손해가 커서, 반복 작업(프롬프트 튜닝)에서 생산성이 급격히 떨어질 수 있습니다.
장애 대응을 설계할 때 “재시도/폴백”을 계층적으로 두는 방식은 인프라·API에서도 동일하게 유효합니다. 접근 방식 자체는 다음 글의 사고법과 유사합니다: Claude API 529 Overloaded 재시도·폴백 설계
5) 흔한 OOM 패턴별 처방
실제로는 OOM 메시지가 뜨는 타이밍이 힌트입니다.
케이스 A: 샘플링 중간(denoise)에서 터짐
- 원인: U-Net 어텐션/피처맵 피크
- 처방:
xFormers우선, 그 다음 해상도/배치 감소
케이스 B: 마지막에 저장/미리보기에서 터짐
- 원인: VAE 디코드 피크
- 처방:
VAE tiling적용, 또는 VAE를 더 가벼운 것으로 교체(환경에 따라)
케이스 C: ControlNet 여러 개 켠 순간부터 불안정
- 원인: 추가 네트워크가 VRAM을 상시 점유
- 처방: ControlNet 수를 줄이거나, 해상도/배치 축소, 가능하면 단계적으로 적용
케이스 D: 같은 설정인데 어떤 시드에서는 되고 어떤 시드에서는 터짐
- 원인: 메모리 단편화(fragmentation) 또는 캐시/피크 변동
- 처방: 프로세스 재시작,
torch.cuda.empty_cache()같은 캐시 정리(코드 실행 환경에서), 타일링으로 피크 낮추기
메모리 단편화는 브라우저 렌더링에서 레이아웃 강제 재계산이 누적되어 성능 경고가 나는 것과 비슷하게 “누적된 상태”가 문제를 키우는 면이 있습니다. 성격은 다르지만, 증상 기반으로 원인을 좁혀가는 방식은 아래 글과 통하는 부분이 있습니다: Chrome Forced reflow 경고 원인·해결 7단계
6) 추천 조합: VRAM별 현실적인 세팅 가이드
GPU VRAM에 따라 “일단 성공하는 조합”이 다릅니다. 아래는 경험적으로 많이 쓰는 방향입니다.
6GB 전후
- 해상도:
512x512또는768계열(모델에 따라) - 필수:
xFormers - 권장: VAE 타일링
- 주의: SDXL 풀 해상도는 상당히 빡빡합니다(특히 ControlNet 동시 사용 시)
8GB
- 해상도: SD 1.5는 여유, SDXL은
1024가 아슬아슬할 수 있음 - 필수:
xFormers - 권장: VAE 타일링(고해상도/업스케일 시)
12GB 이상
- 해상도/배치에 여유가 생기지만, SDXL에 ControlNet·리파이너·업스케일을 한 번에 올리면 여전히 OOM이 납니다.
xFormers는 켜두는 편이 안전합니다.
7) 마무리: xFormers로 “상시 VRAM 절약”, 타일링으로 “피크 VRAM 제거”
정리하면 전략이 깔끔합니다.
xFormers: 확산 과정에서 상시로 VRAM을 아껴주는 기본기(특히 고해상도에서 효과 큼)Tiling: VAE 디코드/인코드 등 특정 구간의 피크 VRAM을 눌러서 “마지막에 터지는” 문제를 해결
둘을 같이 쓰면, 저사양 GPU에서도 SDXL을 현실적으로 굴릴 수 있는 경우가 늘어납니다. 그래도 OOM이 계속된다면, 배치·해상도·ControlNet 수를 줄이는 “구성 자체의 피크”를 먼저 낮추고, 마지막으로 저VRAM 모드나 오프로딩을 고려하는 순서가 시행착오를 줄입니다.