핵심 요약
1
Qwen3.8 Flash Next NVFP4의 현실적인 출발 구성은 B200 ×2였습니다.
2
짧은 요청은 2 RPS, 4K·16K 입력은 동시성 4가 균형점이었습니다.
3
H200 ×2에서는 Marlin W4A16 fallback으로 실행됐지만 9–11 tok/s에 머물렀습니다.
작성: 김진의
Qwen3.8-Flash-Next는 2026년 9월 2일 확인한 Code Arena WebDev 리더보드에서 잠정 점수 1,620점으로 9위에 올랐습니다. 바로 아래에는 GPT-5.6 Sol (xHigh), 12위에는 GLM-5.3-Flash가 있었습니다. 성능표만 보면 직접 띄워 서비스하고 싶어지는 모델인데요. 막상 고민은 모델보다 GPU였습니다.
B200 한 장이면 충분할 줄 알았습니다. 모델도 떴고 첫 장문 응답도 나왔지만, 21K급 요청을 반복하자 추론이 멈췄습니다. 결국 B200 한 장과 두 장, H200 두 장을 비교하며 실제 운영 가능한 구성을 찾아봤습니다.
이 순위표를 보고 궁금해진 것은 코딩 점수를 다시 확인하는 일이 아니라, 이 모델을 직접 운영할 수 있는 GPU 구성이었습니다. 별도의
Inferact/Qwen3.8-Flash-Next-NVFP4 체크포인트를 사용해 반복 요청에서 어느 정도 속도를 유지하는지도 확인했습니다. B200 ×2에서는 짧은 요청 목표 유입률 2 RPS, 4K와 16K 입력 동시성 4를 시작값으로 잡을 만했고, 같은 체크포인트를 올린 H200 두 장은 실행 경로와 속도가 전혀 달랐습니다.모델이 한 번 응답하는지만 보지는 않았습니다. 동시 요청과 장문, 과부하를 걸고 부하를 걷은 뒤 회복하는 과정까지 밀어봤습니다. 궁금했던 건 “B200 두 장이면 뜬다”는 사실보다 어느 설정으로 시작해야 하고, 어디부터 GPU가 아니라 대기열이 문제가 되는가였습니다.
6B 활성 모델이 170GiB 체크포인트가 된 이유
Qwen3.8 Flash Next는 토큰마다 약 6B 파라미터를 활성화하는 MoE 모델입니다. 숫자만 보면 비교적 가벼워 보이지만, 전체 구조는 125B language model body와 51B N-gram embedding을 포함합니다. vLLM 공식 레시피는 native context를 262,144 tokens로 설명하고, N-gram embedding을 CPU로 내릴 때도 51GB 이상의 host memory가 필요하다고 안내합니다.
이번에 사용한
Inferact/Qwen3.8-Flash-Next-NVFP4 revision의 실제 저장 크기는 약 170.2GiB였습니다. 활성 파라미터가 6B라는 사실만 보고 단일 GPU 배포를 예상하기 어려운 이유죠.처음부터 B200 두 장을 고른 것은 아닙니다. 앞선 RTX PRO 6000 96GB 한 장 실험에서는 별도 NVFP4 checkpoint와 vLLM 0.28.0을 사용해 API 응답까지 확인했지만, 실제로 선택된 backend는
MARLIN이었습니다. 이어서 같은 Inferact checkpoint를 올린 B200 한 장은 FLASHINFER_TRTLLM native backend와 첫 장문 응답까지 갔지만, 같은 프로세스에서 21K급 요청을 반복하자 추론이 멈췄습니다. 그래서 PLE CPU offload를 빼고 tensor parallel 2로 나눈 B200 ×2를 다음 후보로 잡았습니다.B200 두 장에서 잡힌 native NVFP4
실험 환경은 아래와 같습니다.
Pod 생성부터 OpenAI 호환 API가 준비되기까지 21분 23초가 걸렸습니다. 모델 다운로드가 11분 7초로 가장 길었고, weight shard 로딩은 79.47초, compile은 27.82초였습니다.
GPU 한 장당 weight는 85.34GiB였습니다. 프로파일링 중 activation peak는 47.8GiB, KV cache 할당은 26.67GiB였고, 실제 지속 부하 중 메모리는 119,606MiB, 약 116.8GiB에서 유지됐습니다. vLLM이 계산한 KV cache의 이론상 용량은 약 195만 tokens였습니다. 그런데 실제 서비스 한계는 이 공간이 차기 전에 나타났습니다. 이번 구성에서는 메모리보다
max_num_seqs=4, 연산 처리량과 요청 대기 시간이 먼저 경계를 만들었습니다.동시성 8부터 늘지 않은 짧은 요청 처리량
512 input → 128 output 합성 요청을 closed-loop로 보냈습니다. 클라이언트 동시성을 1에서 32까지 올리면서 output throughput과 p95 TTFT를 측정했습니다.
동시성 8에서 약 500 output tok/s에 도달한 뒤 처리량은 사실상 늘지 않았습니다. 동시성 16과 32에서는 완료한 토큰 수가 늘어난 것이 아니라, 요청이 서버 큐에서 기다리는 시간이 길어졌습니다. GPU 사용률을 더 높이는 것과 사용자에게 더 빨리 응답하는 것이 같은 말은 아니었습니다.
실제 유입에 가깝게 Poisson 분포로 요청을 보낸 open-loop 테스트에서는 이 경계가 더 선명했습니다.
목표 유입률을 4 RPS에서 5 RPS로 올려도 실제 완료율은 3.218에서 3.361 RPS로 조금만 늘었습니다. 반면 p95 TTFT는 3.80초에서 6.62초로 뛰었습니다. 이번 구성에서 관측한 완료율의 포화 구간은 약 3.3 RPS였고, 대화형 API의 시작값으로는 2 RPS가 더 적합했습니다. 3 RPS는 실제 트래픽에서 p95가 허용 범위 안에 남는지 확인하며 올릴 다음 단계입니다.
4K와 16K 입력의 균형점
입력이 길어지면 동시성 8의 의미가 달라졌습니다.
4K 입력은 동시성 8에서 처리량이 가장 높았지만 p95 TTFT가 4.34초까지 늘었습니다. 동시성 16으로 올리자 처리량마저 줄었습니다. 16K에서는 경계가 더 일찍 나타났습니다. 동시성 8이 동시성 4보다 느렸고, p95 TTFT는 1.87초에서 9.46초로 커졌습니다.
대화형 서비스라면 4K와 16K 모두 동시성 4가 현실적인 시작점입니다. 처리량만 최대화하는 비동기 작업이라면 4K에서 동시성 8을 검토할 수 있지만, 사용자가 첫 토큰을 기다리는 서비스에는 대가가 컸습니다.
16K 장문 생성 중간에 짧은 요청을 연속으로 보낸 테스트에서는 짧은 요청의 TTFT가 0.54–1.54초 범위에 머물렀습니다. 긴 요청 하나가 짧은 요청을 완전히 막는 현상은 보이지 않았습니다.
32K 경계의 즉시 거절
최대 길이 근처에서는 요청이 실패했을 때 엔진이 어떻게 반응하는지도 봤습니다. 32,500 input + 128 output 요청 두 개는 완료됐습니다. 32,650 input + 128 output은 설정한 32,768-token 한계를 넘겨 HTTP 400으로 바로 거절됐습니다. 엔진이 죽거나 OOM이 발생하지 않았고, 직후의 짧은 요청도 3/3 성공했습니다.
30,900-token 입력을 동시성 4로 보낸 테스트도 8/8 성공했습니다. 다만 p95 TTFT는 3.51초, p95 inter-token latency는 98.8ms까지 올라갔습니다. 32K에 들어간다는 것과 32K 요청을 여러 개 동시에 처리하기 좋다는 것은 다른 이야기였습니다. 30K급 요청은 동시성 1에서 시작하거나 장문 전용 큐로 분리하는 편이 낫습니다.
10분 동안 유지된 2 RPS
짧은 요청을 목표 2 RPS로 10분간 보낸 지속 부하에서는 1,200건이 모두 완료됐습니다.
초반 200건과 마지막 200건을 비교했을 때 지연 시간이 누적해서 나빠지지 않았습니다. GPU 메모리는 양쪽 모두 119,606MiB로 유지됐고, 평균 GPU 사용률은 각각 69.98%, 73.58%였습니다. 관측된 KV cache 사용률 최고치는 0.876%였습니다. 부하를 제거한 뒤 보낸 단일 요청 5건도 모두 완료됐고 p95 TTFT는 177ms로 돌아왔습니다.
순간 동시 요청 32건도 32/32 완료됐습니다. 대신 p95 TTFT는 8.24초였습니다. 버스트를 잃지 않고 받아냈지만 빠르게 처리한 것은 아니죠. 약 16K 입력에서 긴 스트림 8개를 2초 뒤 동시에 끊었을 때는 20초 안에 running, waiting, KV 사용량이 모두 0으로 돌아왔고, 이후 짧은 요청도 정상 처리됐습니다.
B200 ×2는 이번 설정에서 2 RPS를 안정적으로 유지했고 순간 부하는 큐로 흡수했지만, 실제 완료율 약 3.3 RPS부터는 처리량보다 대기 시간이 빠르게 늘었습니다.
H200 두 장에서 실행된 Marlin W4A16
그렇다면 H200 두 장은 어떨까요? 같은 NVFP4 checkpoint와 vLLM image를 사용하되 최대 길이를 16,384,
max_num_batched_tokens를 1,024, max_num_seqs를 1로 줄이고 eager mode로 실행했습니다.서버는 정상적으로 올라왔습니다. 다만 로그에 선택된 backend는
FLASHINFER_TRTLLM이 아니라 MARLIN이었습니다. vLLM ModelOpt 문서는 native FP4 GEMM kernel이 없는 GPU에서 NVFP4 checkpoint를 Marlin weight-only W4A16으로 실행한다고 설명합니다. NVIDIA NIM 문서도 Hopper의 NVFP4 emulation이 FP4 weight를 BF16으로 풀어 계산하는 경로라고 명시합니다.H200 한 장당 weight와 non-torch memory는 85.24GiB, KV cache는 47.33GiB였습니다. 벤치마크 뒤 사용량은 138,331MiB였고 남은 여유는 약 5.3GiB였습니다.
H200 두 장에도 checkpoint는 들어갔지만, 짧은 요청의 decode도 11 tok/s에 머물렀습니다. 8K 입력에서는 8.91 tok/s였습니다. H200에서 4-bit weight를 사용할 수 없는 것이 아니라, B200의 native NVFP4 W4A4와 같은 가속을 얻지 못한 것입니다.
compile을 켜자 모델 로딩 전에 멈췄다
eager mode가 느렸기 때문에 설정을 그대로 둔 채
--enforce-eager만 제거해 compile과 CUDA Graph를 다시 켰습니다. vLLM은 VLLM_COMPILE, FULL_AND_PIECEWISE CUDA Graph와 capture size 1·2를 인식했습니다.하지만 두 번의 실행 모두 모델 weight를 읽기 전에 멈췄습니다.
US-NC-1과 EUR-IS-4의 서로 다른 H200 ×2 Pod에서 ncclCommInitRank가 NCCL error: unhandled cuda error를 반환했습니다. 첫 Pod에서는 자동 재시도 두 번, 두 번째 Pod에서는 한 번 같은 오류가 반복됐습니다.여기서는 compile로 얼마나 빨라지는지 측정할 수 없었습니다. 오류가 난 시점이 weight loading, torch.compile, CUDA Graph capture보다 앞이었기 때문입니다. 현재 고정한 image와 H200 host 조합에서 실제 생성까지 확인된 경로는 eager Marlin W4A16이었습니다.
워크로드별 시작 설정
처음부터 최대 처리량을 노리기보다 아래처럼 시작하는 편이 좋습니다.
목표 4 RPS부터는 실제 완료율보다 p95 TTFT가 더 빠르게 악화됐습니다. 이때는 GPU를 더 바쁘게 만드는 것보다 API 앞단에서 유입률을 제한하고
waiting 요청 수를 확인해야 합니다. 짧은 요청 위주라면 2 RPS로 시작해 p95 TTFT가 서비스 목표 안에 있을 때만 3 RPS로 올리는 방식이 안전합니다.재현 명령
아래는 B200 ×2 부하 테스트에 사용한 주요 실행 옵션입니다. 모델 revision과 image digest까지 고정해야 같은 실행 경로를 확인하기 쉽습니다.
명령을 직접 복사하지 않아도 됩니다. Runpod 공개 템플릿을 사용하면 같은 image digest와 실행 옵션이 적용됩니다. 배포 화면에서 NVIDIA B200을 선택하고 GPU Count를 2로 설정해야 합니다.
vllm serve Inferact/Qwen3.8-Flash-Next-NVFP4 \
--revision 103a7608316173ca6edd49929544244de7ffda70 \
--host 0.0.0.0 \
--port 8000 \
--served-model-name qwen3.8-flash-next-nvfp4 \
--tensor-parallel-size 2 \
--distributed-executor-backend mp \
--dtype bfloat16 \
--quantization modelopt_fp4 \
--gpu-memory-utilization 0.90 \
--max-model-len 32768 \
--max-num-batched-tokens 4096 \
--max-num-seqs 4 \
--no-enable-prefix-caching \
--no-enable-flashinfer-autotune \
--language-model-only \
--generation-config vllm \
--reasoning-parser qwen3 \
--enable-auto-tool-choice \
--tool-call-parser qwen3_coderQwen3.8 Flash Next를 굴린다면 B200 두 장부터
이번 결과만 놓고 Qwen3.8 Flash Next NVFP4를 직접 굴린다면 B200 두 장으로 시작하겠습니다. H200 두 장에도 checkpoint는 들어갔지만 Marlin fallback에서는 9–11 tok/s에 머물렀고, B200 한 장은 반복 장문 요청에서 멈췄습니다. B200 ×2는 native NVFP4로 짧은 요청부터 32K 입력, 지속 부하와 버스트까지 처리했습니다.
다만 트래픽 설정은 워크로드에 따라 달라집니다. 짧은 요청이 중심이라면 2 RPS부터 시작할 수 있고, 4K·16K 입력은 동시성 4, 30K 안팎의 장문은 동시성 1이나 별도 큐가 적합했습니다. GPU 구성은 B200 ×2로 잡되, 요청 길이에 맞춰 유입률과 동시성을 조정하는 것이 이번 실험에서 얻은 가장 현실적인 답입니다.


