DGX Spark 2대, 1M 컨텍스트 도전기 2. (부팅은 정복, 1M은 여전히 벽)
지난번 글은 '뭐 별 수 있겠어'로 닫았다. 완전 1M은 day-0 빌드의 한계이고, SGLang이 Qwen4Exp의 1M을 안정화하면 다시 도전하자고. 그런데 며칠 뒤, 정답 레시피가 나왔다. 우리가 못 뚫던 그 977K 멈춤의 원인을 이름까지 붙여 문서화한 레시피였다. 이 글은 그 레시피로 다시 뚫으러 갔던 기록이다.
정답 레시피가 나왔다
MiaAI-Lab/Qwen3.8-Flash-Next-Dual-DGX-Sparks, 공개 이틀 만에 별 171개를 받았다. 원인은 두 가지로 정리되어 있었다.
하나는 SM121이다. FlashInfer의 TRT-LLM sparse-decode 경로가 장문 디코드를 조용히 손상시킨다. 120k, 190k, 210k에서 토큰 id 0으로 떨어진다. sglang#36537에 그 증상이 실려 있다. packed FA4 CuTe는 GB10에서 컴파일조차 안 된다. 정답은 우리 전편 패치와 정반대였다. 우리는 is_sm120_supported()로 그 경로를 강제했는데, 정답은 SM121에서 그 경로를 끄고 Triton packed-varlen fallback을 쓰는 것이었다. sglang#36845.
다른 하나가 977K 멈춤의 진짜 원인이다. QSA indexer의 prefill workspace가 fp32로 [chunk × history] 로짓 행렬을 레이어마다 통째로 만들어 낸다. chunk 4096에 history 300k면 임시 메모리만 8~10GB. 그게 레이어마다 쌓여 박스를 웨징하는 식이었다. 여기에 CHUNKED_PREFILL_SIZE=1024를 주면 풀린다고 적혀 있었다.
NVFP4 KV 캐시도 이 레시피의 축이다. 아키텍처 고유 기능인데, 1M YaRN 기준으로 KV 풀이 2,851,328 토큰까지 들어간다. bf16의 925K보다 2.72배다. 1M을 담으려면 이쪽이 필요하다. 메모리 비율은 0.82에 mamba ratio 0.3. 전편에서 0.90으로 밀어붙인 것보다는 여유가 있었다.
재도전
start.sh를 받아 .env만 우리 클러스터로 바꿨다. 두 노드, 200G RoCE, MTU 9000. doctor가 이 구성을 전부 통과했다. YaRN 1M과 NVFP4 KV 설정도 자동으로 잡혔다.
이번엔 캐시를 다시 받지 않았다. 기존 126G짜리 HF 캐시를 그대로 쓸 수 있었고, 419개 파일 135.25GB 검증에 재다운로드는 0바이트였다. worker 쪽 캐시도 이미 동기화되어 있어서 받을 게 아무것도 없었다. SM121 Triton fallback과 NVFP4 KV 패치를 넣은 이미지를 양 노드에서 빌드하고 부팅을 걸었다.
부팅은 정복했다. context 1048576, YaRN factor 4.0, kv_cache_dtype=nvfp4. KV 풀이 3,110,400 토큰까지 올라왔다. 전편의 1,632,448 토큰에서 거의 두 배로 늘었다. CUDA graph 캡처 전부 OK. 모델 로딩 441.84초. 성공한 부팅 옵션의 핵심만 적어두면 이렇다.
context length 1048576 (YaRN factor 4.0)
kv cache dtype nvfp4 (KV pool 3,110,400, float4_e2m1fn_x2)
CHUNKED_PREFILL_SIZE 1024
mem fraction 0.82 · mamba ratio 0.3
load time 441.84s
이때만 해도 기분이 꽤 좋았다.
977K, 여전히 벽
그리고 977K 단일 needle prefill. 전편에서 쓰던 그 976,837 토큰 문서를 그대로 다시 쐈다. scheduler가 GPU 0%, CPU 90%로 8분을 돌다가 SIGTERM으로 죽었다. Subprocess scheduler_0 crashed with exit code -15. 전체가 종료되고 500이 떨어졌다. 쩝.
형태가 달라진 것만은 분명했다. 전편은 chunk 4096에서 500과 coredump였고, 이번은 chunk 1024에서 CPU-bound SIGTERM이었다. 여러 가지를 바꿔도 1M 단일 prefill만은 그대로 벽이다. 좋아진 것은 부팅과 KV, 그래프까지였다. day-0 SGLang 빌드에서 1M 단일 prefill은 아직 안정화되지 않았다. 전편의 day-0 한계 결론을 실측으로 다시 확인한 꼴이다.
곁다리로 본 GLM
재도전이 막힌 동안, GLM-5.3-Flash도 한번 들여다봤다. 2대의 Spark에서 1M을 굴릴 수 있는지가 궁금했다. 결론부터 말하면 아니다.
퀀타이즈는 있었다. LibertAIDAI의 NVFP4 버전이 598.5GiB를 181GiB로 줄여 준다. 320B/18B 멀티모달이고, 비전타워와 어텐션, 임베딩은 BF16이 남아 있으며 routed expert만 97% 양자화다. tool-eval 실측이 FP8의 90점에서 85점으로 빠지긴 했지만, 감당할 만한 손해였다.
문제는 퀀타이즈가 아니라 아키텍처였다. 34개 KDA 선형어텐션 레이어가 요청마다 재귀 상태, 상태캐시를 요구한다. 컨텍스트와 동시성이 직접 경쟁하는 구조다. 2대에서 max-req 8에 131K를 주면 상태캐시가 넘친다. 안정적인 조합은 65K에서 131K 사이다. 1M은 4대에서만 가능하고, 그때도 30에서 43 tok/s다. 레시피로는 못 바꾸는 근본 제약이다.
결론
정리하면 이렇다. Flash-Next의 1M은 부팅, KV, CUDA graph까지 정복했다. 그런데 단일 1M prefill이 day-0 한계에 그대로 남아 있다. GLM의 1M은 2대로 구조적으로 불가능하다. 그러니 실전 1M으로 검증된 DS4-0731 유지가 여전히 맞다. 1M 실전은 그 모델이 가고, 1M 미만의 인터랙티브와 에이전트만 Flash-Next 262K가 후보다. 전편과 결론이 똑같아졌다.
다음 관문은 하나다. SGLang이 Qwen4Exp의 1M을 안정화해서 릴리스하는 일. sglang#36497이 그 이슈다. 그것이 나오면 다시 도전한다. 그때까지는 지금 조합으로 버틴다. 다음엔 됐으면 좋겠다. 그건 그때 가서 보자.