FLUX.2-dev GGUF Q4_K_M, 애플 실리콘에서 29GB VRAM 사용 이유 분석
양자화된 GGUF Q4_K_M 형식의 FLUX.2-dev 모델은 이론적으로 약 19GB의 메모리를 사용해야 하지만, M3 Pro 칩이 탑재된 애플 실리콘에서는 최대 28.92GB까지 증가합니다. 분석 결과, 추가적인 메모리 사용은 MPS 오버헤드, 가중치 재양자화, 그리고 통합 메모리 내 CPU와 GPU 간 데이터 전송에 기인합니다.
로딩 아키텍처와 순차적 오프로드
FLUX.2-dev는 전체 정밀도에서 45GB VRAM이 필요한 Mistral-Small-3.2-24B 텍스트 인코더를 사용합니다. 애플 실리콘 환경에서는 MLX를 통해 4비트 버전이 사용되며, 이는 16GB의 메모리를 소모합니다. 프로세스는 순차적 오프로드 패턴을 따릅니다:
- MLX 인코더 로드 (~13GB)
- CPU에서 프롬프트를 임베딩으로 변환
- 인코더 오프로드 및 캐시 정리
- 디스크에서 DiT 모델 (GGUF Q4_K_M) 로드 (~19GB)
- CPU에서 VAE와 스케줄러 초기화
- 전체 파이프라인을 MPS로 전달
이론적 피크는 개별 컴포넌트 크기의 최댓값이지만, 통합 메모리 동작 방식으로 인해 현실과 다릅니다.
테스트 환경
- macOS: 26.3.1 (Tahoe)
- 칩: Apple M3 Pro, 36GB 통합 메모리
- Python: 3.14.3
- PyTorch: 2.11.0
- diffusers: 0.38.0.dev
- mlx-lm: 0.31.1
프로파일링은 phys_footprint (Activity Monitor와 유사) 및 PyTorch의 MPS 메트릭을 사용했습니다. psutil.rss는 MPS 할당을 반영하지 않습니다.
상세 메모리 사용량 분석
단계별 측정값
| 단계 | phys_footprint | MPS 드라이버 | MPS 현재 사용량 |
|-------|----------------|------------|-------------|
| 기본 상태 | 0.27 GB | 0 | 0 |
| MLX 인코더 로드 | 13.22 GB | 0 | 0 |
| GGUF CPU 로드 | 19.23 GB | 0 | 0 |
| pipe.to(mps) | 23.79 GB | 20.06 GB | 18.75 GB |
| 중간 추론 시점 | 26.50 GB | 25.62 GB | 18.78 GB |
28.92GB의 피크는 pipe.to("mps") 과정에서 발생하며, 이는 CPU와 MPS 모두에서 데이터 복사본이 동시에 존재하기 때문입니다.
28GB 피크의 구성 요소
- DiT Q4_K_M (uint8): 18.59 GB
- VAE (bf16): 0.16 GB
- MPS 풀 오버헤드: +1.31 GB
- IOKit/Metal 그래프 캐시: ~3.73 GB (셰이더, 페이지 테이블, 리무보 버퍼)
GGUF 가중치는 MPS에서 uint8 그대로 유지되며, 포워드 패스 중 레이어 단위로 bf16으로 재양자화됩니다. 임시 bf16 텐서(최대 6.92GB)는 캐시에 누적됩니다.
피크 메모리 감소를 위한 최적화 전략
- 단계별 MPS 전송: 매 30개 파라미터마다
gc.collect()를 호출하며 하나씩 파라미터를 이동합니다.
def _move_to_device_incremental(pipe, device):
n = 0
for attr in list(vars(pipe)):
comp = getattr(pipe, attr, None)
if not isinstance(comp, torch.nn.Module):
continue
for param in comp.parameters():
param.data = param.data.to(device)
n += 1
if n % 30 == 0:
gc.collect()
for buf in comp.buffers():
buf.data = buf.data.to(device)
gc.collect()
torch.mps.synchronize()
torch.mps.empty_cache()
- 단계 간 empty_cache() 활용: 추론 단계 중 콜백을 통해
torch.mps.empty_cache()를 사용하여 재양자화 캐시를 비웁니다.
- 생성 후 캐시 플러시: 추론 후
gc.collect()를 실행한 다음torch.mps.empty_cache()를 호출합니다.
최적화 효과 비교
| 지표 | 전 | 후 | 차이 |
|--------|--------|-------|-------|
| 피크 phys_footprint | 28.92 GB | 27.36 GB | -1.56 GB |
| 추론 후 안정 상태 | 26.25 GB | 21.76 GB | -4.49 GB |
| 생성 시간 (1단계) | 2:04 | 2:11 | +7초 |
피크 감소는 로딩 중 단계적 가비지 컬렉션 덕분이며, 안정 상태 개선은 캐시 플러시로 인한 것입니다. 최적화 불가능한 MPS/IOKit 오버헤드는 여전히 약 3.5GB 수준입니다.
핵심 요약:
- 안정 상태 최소값: ~24GB, 추론 피크: ~27GB
- 주요 비용: 18.59GB의 uint8 DiT 가중치 + 3.5GB MPS 오버헤드
- bf16 재양자화로 인한 임시 버퍼 추가: 3–5GB
- CPU→MPS 전송은 통합 메모리로 인해 피크를 두 배로 증가시킴
- 최적화로 피크 -1.5GB, 안정 상태 -4.5GB 감소, 품질 손실 없음
FLUX.2-dev GGUF 모델의 실제 최소 요구 메모리는 M3 Pro 기준 24+ GB의 통합 메모리입니다. 더 작은 구성에서는 보다 강력한 양자화 또는 CPU 오프로드가 필요합니다.
— Editorial Team
아직 댓글이 없습니다.