Benchmarks 목록으로
Source URL 유지 · Benchmarks projection
DGX Spark 실전기: Qwen3.6을 llama.cpp에서 vLLM + DFlash로 갈아타기 — 대표 이미지

DGX Spark 실전기: Qwen3.6을 llama.cpp에서 vLLM + DFlash로 갈아타기

2026년 4월 26일업데이트 2026년 8월 24일8
작성 DevSnack Lab원문·측정 조건·검수 범위는 본문에 기록
22
최적화추론 가속DGX SparkLLMvLLM
SEO meta description: DGX Spark에서 Qwen3.6 35B-A3B MoE 모델을 llama.cpp에서 vLLM + DFlash 스펙큘러티브 디코딩으로 전환하며 얻은 성능 향상 실제 경험.
Google Blogger HTML — "HTML 보기"에 통째로 붙여넣기 제목(Title) 필드: DGX Spark 실전기: Qwen3.6을 llama.cpp에서 vLLM + DFlash로 갈아타기 라벨(Labels): DGX Spark, vLLM, llama.cpp, Qwen3.6, NVFP4, DFlash, 로컬 LLM, AI 추론, 스펙큘러티브 디코딩, Blackwell, GB10, 양자화, Docker, 벤치마크, 실전기 한줄 요약
TL;DR — llama.cpp로 Qwen3.6을 잘 쓰고 있었는데, vLLM + NVFP4 + DFlash 조합이 정말 2배 빠를까? DGX Spark에서 직접 설치하고, 삽질하고, 측정했다. 결론부터 말하면 — 빠르다. 88~104 tok/s.
목차 ==================== 본문 시작 ====================

발단: 숫자가 너무 좋아서 의심스러웠다

llama.cpp로 Qwen3.6-35B-A3B을 쓰는 건 편하다. GGUF 파일 하나 받아서 llama-server 띄우면 끝이니까요. Q4_K_M 양자화 기준으로 싱글 스트림 40~50 tok/s 정도가 나오는데, 실사용에서 충분히 쾌적한 속도이다.

Qwen3.6 llama.cpp에서 vLLM DFlash로 전환 후 DGX Spark 성능 벤치마크 비교 차트

그런데 허깅페이스를 돌아다니다가 AEON-7이라는 사람이 올려놓은 벤치마크를 봤다. 같은 Qwen3.6-35B-A3B을 NVFP4 양자화하고 DFlash라는 스펙큘러티브 디코딩을 붙여서 싱글 스트림 83.9 tok/s, 정상 상태 115~118 tok/s를 찍었다고 한다. llama.cpp 대비 2~3배라는 건데, 직접 확인하지 않으면 믿기 어려운 수치였다.

그래서 직접 해봤다.

현재 환경: llama.cpp 기준선

비교를 위해 먼저 llama.cpp 환경을 정리한다. DGX Spark에서 Qwen3.6-35B-A3B GGUF를 돌릴 때의 일반적인 성능이다.

llama-server \
  -m Qwen3.6-35B-A3B-UD-Q4_K_M.gguf \
  -ngl 999 -c 32768 -fa \
  --host 0.0.0.0 --port 8080

NVIDIA 개발자 포럼에 보고된 DGX Spark 실측 데이터를 종합하면, Q4_K_M에서 대략 45~50 tok/s, Q6_K_XL에서 38~45 tok/s, MXFP4_MOE에서 45~55 tok/s 정도가 나온다. 모델 크기는 12~20GB 범위로, 128GB 통합 메모리에서 아주 여유롭게 돌아간다.

llama.cpp의 강점은 단순함이다. GGUF 파일 하나, 바이너리 하나면 돌아간다. Docker도 필요 없고, 특별한 설정도 없다. 5분이면 모델을 돌릴 수 있다.

vLLM + NVFP4 + DFlash 설치: 실전 과정

사전 확인

# GPU 확인 — "NVIDIA GB10"이 보여야 함
nvidia-smi

# Docker + NVIDIA Container Toolkit
docker run --rm --gpus all nvidia/cuda:13.0-base-ubuntu24.04 nvidia-smi

# 디스크 여유 — 최소 50GB 필요
df -h /opt
⚠️ 이 Docker 이미지는 DGX Spark(GB10, SM 12.1) 전용이다. H100, A100, B200, RTX 5090 등 다른 GPU에서는 동작하지 않는다.

모델 다운로드

두 개를 받아야 한다. NVFP4 양자화된 타겟 모델(~22GB)과 DFlash 드래프터(~905MB)이다.

huggingface-cli가 "command not found"로 안 되는 경우가 많은데, 몇 가지 대안이 있다.

sudo mkdir -p /opt/qwen36 && sudo chown $USER:$USER /opt/qwen36
cd /opt/qwen36

방법 1: Python 스크립트 (가장 확실) — CLI 도구의 PATH 문제나 xet 백엔드 충돌을 완전히 우회한다.

pip install -U huggingface_hub hf_transfer

HF_HUB_ENABLE_HF_TRANSFER=1 python3 << 'EOF'
from huggingface_hub import snapshot_download

snapshot_download(
    repo_id="AEON-7/Qwen3.6-35B-A3B-heretic-NVFP4",
    local_dir="/opt/qwen36/qwen36-nvfp4",
    local_dir_use_symlinks=False
)
snapshot_download(
    repo_id="z-lab/Qwen3.6-35B-A3B-DFlash",
    local_dir="/opt/qwen36/qwen36-dflash",
    local_dir_use_symlinks=False
)
print("다운로드 완료!")
EOF

방법 2: hf 명령어 — 작동한다면 더 간단하다.

pip install -U huggingface_hub[hf_xet]
export HF_HUB_ENABLE_HF_TRANSFER=1
hf download AEON-7/Qwen3.6-35B-A3B-heretic-NVFP4 --local-dir ./qwen36-nvfp4
hf download z-lab/Qwen3.6-35B-A3B-DFlash --local-dir ./qwen36-dflash

방법 3: Git LFS

sudo apt install git-lfs && git lfs install
git clone https://huggingface.co/AEON-7/Qwen3.6-35B-A3B-heretic-NVFP4 ./qwen36-nvfp4
git clone https://huggingface.co/z-lab/Qwen3.6-35B-A3B-DFlash ./qwen36-dflash
rm -rf ./qwen36-nvfp4/.git ./qwen36-dflash/.git  # 디스크 절약
⚠️ 2026-04-19 이전에 DFlash 드래프터를 받은 적이 있다면 반드시 삭제하고 다시 받아야 한다. 이전 버전에는 16K 토큰 이후 크래시를 유발하는 버그가 있었다.

Docker 이미지

docker pull ghcr.io/aeon-7/vllm-spark-omni-q36:v1.2

약 9GB 압축 이미지이다. vLLM HEAD 소스 빌드(cu130/sm_120), FlashInfer 0.6.8, SM121 대응 8개 패치가 모두 포함되어 있다.

첫 번째 삽질: 오토튜닝으로 시스템 마비

처음에는 AEON-7의 공식 docker-compose.yml을 그대로 사용했다. max-model-len 262144, max-num-seqs 128, gpu-memory-utilization 0.85. 서버가 올라오는 과정에서 torch.compile과 autotuning이 돌아가기 시작했는데, 이 과정에서 시스템이 완전히 마비됐다. SSH도 안 먹히고, 전원 버튼으로 강제 재부팅하는 수밖에 없었다.

DGX Spark의 128GB 통합 메모리는 GPU와 CPU가 공유하기 때문에, vLLM이 메모리를 과도하게 잡아먹으면 OS 자체가 움직일 수 없게 된다. 공식 벤치마크 설정은 "이 머신을 이것만을 위해 쓴다"는 전제 하의 설정이었던 거다.

해결책은 두 가지였다. 오토튜닝을 끄고, 메모리를 보수적으로 잡는 것이다.

안정적인 설정: 실사용을 위한 docker-compose.yml

services:
  vllm:
    image: ghcr.io/aeon-7/vllm-spark-omni-q36:v1.2
    container_name: vllm-qwen36-heretic
    restart: unless-stopped
    network_mode: host
    environment:
      - VLLM_ALLOW_LONG_MAX_MODEL_LEN=1
      - TORCH_MATMUL_PRECISION=high
      - PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
      - NVIDIA_FORWARD_COMPAT=1
      - VLLM_TEST_FORCE_FP8_MARLIN=1
      - TORCHINDUCTOR_MAX_AUTOTUNE=0
      - TRITON_MAX_AUTOTUNE=0
      - VLLM_USE_V1=1
    volumes:
      - /opt/qwen36/qwen36-nvfp4:/models/qwen36
      - /opt/qwen36/qwen36-dflash:/models/qwen36-dflash
    command:
      - bash
      - -c
      - |
        exec vllm serve /models/qwen36 \
          --served-model-name qwen36-think qwen36-fast \
          --host 0.0.0.0 --port 8080 \
          --tensor-parallel-size 1 \
          --dtype auto \
          --quantization compressed-tensors \
          --max-model-len 131072 \
          --max-num-seqs 2 \
          --max-num-batched-tokens 4096 \
          --gpu-memory-utilization 0.50 \
          --enable-chunked-prefill \
          --enable-prefix-caching \
          --trust-remote-code \
          --enable-auto-tool-choice \
          --tool-call-parser qwen3_coder \
          --reasoning-parser qwen3 \
          --speculative-config '{"method":"dflash","model":"/models/qwen36-dflash","num_speculative_tokens":15}' \
          --attention-backend flash_attn
    ipc: host
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]

공식 설정과 무엇이 달라졌나

TORCHINDUCTOR_MAX_AUTOTUNE=0TRITON_MAX_AUTOTUNE=0은 시스템 마비를 일으킨 오토튜닝을 끈다. gpu-memory-utilization0.85에서 0.50으로 낮췄다. 124GB의 50%인 약 62GB만 vLLM에 할당하고, 나머지 62GB는 다른 작업을 위해 남겨둔다. max-model-len은 262144에서 131072(128K)로, max-num-seqs는 128에서 2로, max-num-batched-tokens는 65536에서 4096으로 줄였다. 혼자 에이전트용으로 쓸 거라면 이 정도면 충분하다.

💡 VLLM_TEST_FORCE_FP8_MARLIN=1필수이다. GB10의 SM12.1에서 NVFP4 MoE를 돌릴 때 유일하게 작동하는 백엔드가 Marlin이기 때문이다. 이 환경변수가 없으면 다른 백엔드가 시도되다가 실패한다.

핵심 트릭: 모델 이름으로 Thinking On/Off 분기

이 설정에서 가장 마음에 드는 부분이다. vLLM의 --served-model-name에 이름을 두 개 등록했다.

--served-model-name qwen36-think qwen36-fast

물리적으로는 같은 모델이지만, 클라이언트에서 어떤 이름으로 호출하느냐에 따라 다르게 쓸 수 있다.

qwen36-think으로 호출하면 Thinking 모드가 켜져서, 모델이 <think> 태그 안에 추론 과정을 먼저 생성한 뒤 최종 답변을 내놓는다. 수학, 코딩, 복잡한 추론에 유리하다.

qwen36-fast로 호출하면 Thinking 없이 바로 답변한다. 일반 대화나 빠른 응답이 필요할 때 사용한다.

서버에서 기본 Thinking을 끄고 싶다면 다음 플래그를 추가한다.

--default-chat-template-kwargs '{"enable_thinking": false}'

이렇게 하면 qwen36-fast는 기본적으로 Thinking OFF, qwen36-think으로 호출할 때 클라이언트에서 "chat_template_kwargs": {"enable_thinking": true}를 보내면 된다.

Hermes.ai 같은 에이전트 프레임워크에서는 모델을 두 개 등록해놓고 작업 성격에 따라 골라 쓸 수 있다. 코딩 에이전트는 qwen36-think, 단순 응답은 qwen36-fast. 메모리 추가 사용 없이 하나의 서버에서 두 가지 성격의 모델을 제공하는 셈이다.

실측 결과

모든 측정은 실제 DGX Spark(ASUS GX10, 128GB, GB10)에서 수행했다.

vLLM + NVFP4 + DFlash 속도

테스트모드생성 토큰소요 시간속도
quicksort (영어)think ON1,01511.47초88.4 tok/s
quicksort (영어)think OFF8378.66초96.6 tok/s
소수 찾기 (한국어)think ON2,04719.64초104.3 tok/s
quicksort (영어, 이전 설정)think ON2,04821.52초95.1 tok/s

Thinking ON과 OFF에서 tok/s 자체는 비슷하지만(88~96 tok/s), Thinking OFF일 때 생성하는 총 토큰 수가 적기 때문에 체감 응답 시간은 훨씬 빠르다. quicksort 예제에서 11.47초 vs 8.66초, 약 25% 빠른 체감이다.

llama.cpp vs vLLM 직접 비교

환경양자화모델 크기속도llama.cpp 대비
llama.cpp Q4_K_MINT4 GGUF~12GB45~50 tok/s
llama.cpp Q6_K_XLINT6 GGUF~16GB38~45 tok/s0.9×
llama.cpp MXFP4_MOEMXFP4 GGUF~12GB45~55 tok/s1.1×
vLLM NVFP4 + DFlashNVFP4 W4A4~22GB88~104 tok/s~2×

메모리를 절반(0.50)으로 줄이고 오토튜닝을 꺼도 속도가 유지된다. 공식 벤치마크의 83.9 tok/s(중앙값)을 오히려 넘는 수치가 나왔는데, 동시 요청 수를 2로 줄여서 단일 요청에 리소스가 집중된 덕분으로 보인다.

메모리 사용량 비교

환경메모리 사용남는 메모리
llama.cpp Q4_K_M~12GB~112GB
vLLM NVFP4+DFlash (0.85)~108GB~16GB
vLLM NVFP4+DFlash (0.50)~52GB~72GB

gpu-memory-utilization 0.50 설정이 핵심이다. 52GB만 사용하면서 88~104 tok/s를 유지하고, 72GB를 다른 용도로 남겨둔다. llama.cpp만큼 여유롭진 않지만, 다른 모델이나 작업을 동시에 돌리기에 충분한 여유이다.

속도 차이가 나는 이유

같은 4비트 양자화인데 왜 2배 차이가 나는지 궁금할 수 있다. 세 가지 요인이 결합된 결과이다.

1. 하드웨어 FP4 텐서코어 활용

llama.cpp의 GGUF INT4는 소프트웨어로 디퀀타이징한 뒤 연산하지만, NVFP4는 GB10의 Blackwell FP4 텐서코어가 직접 연산한다. 같은 4비트여도 연산 경로가 근본적으로 다르다.

2. DFlash 스펙큘러티브 디코딩

905MB 크기의 경량 블록 디퓨전 모델이 한 번에 15개 토큰을 병렬 예측하고, 타겟 모델이 이를 한 번에 검증한다. 수학이나 코딩처럼 예측 가능한 패턴에서는 수용률이 62~78%에 달해서, 한 스텝에 평균 2.7~4.4개 토큰이 확정된다.

3. vLLM의 서빙 최적화

연속 배칭, KV 캐시 관리, CUDA 그래프 캡처 등 서버급 추론 최적화가 자동 적용된다.

트러블슈팅 모음

시스템 마비 / 리부트

원인은 거의 항상 메모리이다. gpu-memory-utilization을 0.50 이하로 낮추고, TORCHINDUCTOR_MAX_AUTOTUNE=0TRITON_MAX_AUTOTUNE=0을 반드시 설정하세요. 그래도 마비되면 --enforce-eager를 추가해서 CUDA graph 캡처를 건너뛰세요. 속도가 약간 떨어지지만(~42 tok/s) 안정성이 확보된다.

huggingface-cli가 안 됨

PATH 문제이거나 xet 백엔드 충돌이다. Python 스크립트로 snapshot_download()를 직접 호출하는 게 가장 확실하다. pip uninstall xet-core 후 재시도하는 것도 도움이 된다.

"Your GPU does not have native support for FP4"

로그에 이 경고가 나와도 정상 작동한다. Marlin 커널이 weight-only FP4를 처리하고 있다는 의미이다. 실측 속도가 88~104 tok/s 나오고 있으니 걱정할 필요 없다.

content: null, finish_reason: "length"

Thinking 모드에서 max_tokens가 너무 낮은 경우이다. Qwen3.6은 <think> 태그 안에 추론 과정을 먼저 생성하는데, 이 과정에서 토큰 예산을 소진해버린다. max_tokens2048 이상으로 올리거나, Thinking을 꺼서 해결한다.

visual.blocks... not found WARNING

비전 인코더 가중치 관련 경고이다. 텍스트 전용으로 사용할 경우 무시해도 된다.

결론: 언제 무엇을 써야 하나

시나리오추천이유
빠르게 테스트하고 싶다llama.cpp + GGUF5분이면 돌아감, Docker 불필요
최대 싱글 스트림 속도vLLM + NVFP4 + DFlash88~104 tok/s
에이전트 / tool callingvLLM + NVFP4 + DFlashthink/fast 모드 분기, 병렬 호출 지원
메모리 아껴서 다른 것도 돌려야llama.cpp Q4_K_M12GB면 충분
간편함/안정성 최우선llama.cpp + GGUF단일 바이너리, 설정 거의 없음
128K 장문 맥락 + 속도vLLM + NVFP4 + DFlash128K에서도 속도 유지

llama.cpp와 vLLM은 경쟁 관계가 아니라 용도가 다르다. llama.cpp는 "파일 하나로 바로 돌리는" 도구이고, vLLM은 "서비스를 구축하는" 엔진이다. 현재 제 DGX Spark에서는 vLLM + DFlash를 에이전트 서빙용으로 상시 운영하면서(gpu-memory-utilization 0.50), 남는 72GB로 다른 실험을 돌리고 있다.

다만 이 세팅에는 대가가 있다. Docker 의존성, SM12.1 전용 커스텀 이미지, 8개 패치가 적용된 비표준 vLLM 빌드, DFlash 드래프터 버전 관리, 그리고 첫 세팅에서의 시스템 마비 경험까지. llama.cpp의 단순함과는 거리가 먼 세계이다. 이 복잡성을 감수할 가치가 있는지는 각자의 상황에 달려 있지만, 속도 차이 2배는 한번 경험하면 돌아가기 어렵다.

다음 글 예고

DGX Spark에 172B 파라미터 모델을 우겨넣는 실험을 해볼 예정이다. MiniMax-M2.7-REAP-172B를 NVFP4로 양자화한 체크포인트가 허깅페이스에 올라와 있는데, 92GB짜리 모델을 128GB 메모리에 올리면 과연 쓸 만한 수준의 속도가 나올지 확인해보겠다.

2026년 4월 27일 작성. ASUS GX10 (DGX Spark, GB10, 128GB) 실측 기반.
참고: AEON-7/Qwen3.6-35B-A3B-heretic-NVFP4 · z-lab/Qwen3.6-35B-A3B-DFlash · GitHub 배포 가이드



📖 관련 글