M5 Ultra 256GB에서 llama.cpp로 DeepSeek V4.1 Flash 1017 UD-Q2_K_XL, 128K 위치 생성 초당 41토큰

M5 Ultra 256GB에서 Unsloth UD-Q2_K_XL과 Unsloth 배포 llama.cpp로 DeepSeek V4.1 Flash 1017을 128K까지 쟀습니다. 128K 생성 41 tok/s, prefill 510~534 tok/s였고 DSpark는 코드에서만 빨랐습니다.

ds4 글과 oMLX 글에서 잰 Mac Studio M5 Ultra(GPU 80코어, 통합 메모리 256GB)에서, 이번에는 llama.cpp로 DeepSeek V4.1 Flash 1017을 돌렸습니다. llama.cpp 업스트림에는 10월 26일에 V4.1 지원이 병합됐고, Unsloth는 GGUF 가중치와 함께 자체 패치를 얹은 배포 빌드를 냈습니다. 가중치는 Unsloth UD-Q2_K_XL(분할 7개, 합계 334.8GB)입니다.

작업은 앞의 두 글과 같습니다. 소설과 코드 원문을 32K토큰씩 이어 붙여 128K까지 가며, 단계마다 새로 넣은 약 32K의 prefill과 이어지는 256토큰 생성을 원문마다 3회 쟀습니다. 회차마다 서버 프로세스를 새로 띄웠고, 24단계가 모두 실패나 재측정 없이 끝났습니다.

이 기계에서 잰 DeepSeek V4.1 결과 가운데 공통 벤치마크의 원래 방식을 그대로 따른 것은 이번이 처음입니다. HTTP /completion에 토큰 ID를 보냈고, 단계마다 재사용한 캐시 토큰 수가 기준(33,023, 66,047, 99,071)과 정확히 같았습니다. ds4 결과는 ds4 서버에 토큰 ID 요청 경로가 없어 C 세션 API로 잰 변형이고, oMLX 결과는 앞 단계에서 생성한 토큰을 다시 처리하는 변형입니다. 모델 파일도 셋이 모두 달라서, 아래에 함께 적은 다른 엔진 값은 참고용입니다.

41.32~41.53 tok/s128K 위치 생성 (3회 평균)
510~534 tok/s128K 지점 추가 prefill
약 61~64초128K 지점 첫 토큰까지
1.46배 / 0.71배DSpark를 켰을 때 코드 / 소설 생성 (1회)

사용 설정

항목 값
기계 Mac Studio, M5 Ultra (CPU 12+24코어, GPU 80코어), 256GB, macOS 27.0.1
엔진 Unsloth 배포 llama.cpp b11985-mix-c116c32 (build 11985, commit 6f4cc4d72), Metal
모델 Unsloth DeepSeek-V4.1-Flash-1017-GGUF의 UD-Q2_K_XL (HF revision 8a6ec9cf). 분할 7개의 SHA256을 HF 값과 모두 대조
메모리 배치 엔진 기본(mmap). Engram 표(per_layer_token_embd, 128,910MiB)는 필요한 행만 SSD에서 읽고(lazy read + prefetch), 나머지는 GPU에 매핑(모델 189,697MiB + 계산 6,653MiB)
KV f16(엔진 기본). DeepSeek V4 전용 캐시라 문맥 135,168에서 KV 버퍼가 약 0.5GiB
batch / ubatch 2048 / 2048
Flash Attention on
문맥 135,168 고정, 슬롯 1개, context shift 끔
speculative 대표는 off. DSpark 드래프터 결과는 아래에 따로 적음
생성 greedy, 256토큰, 원문 이어쓰기
반복 회차마다 새 서버 프로세스, 짧은 인사로 워밍업. 로딩(8.2~11.3초)은 통계에서 뺌
전원·메모리 잠자기 방지, 저전력 모드 끔, GPU wired 한도 245,760MiB

엔진은 업스트림 llama.cpp가 아니라 업스트림 b11985에 Unsloth 패치(PR #332, MXFP8 형식과 Engram 표 읽기 등)를 얹은 배포 빌드입니다. 아래 설정 고르기에서 같은 설정의 업스트림 b12008보다 생성이 17~20% 빠르고 prefill은 약 2% 느렸습니다. 두 빌드 사이에는 b11985에서 b12008까지의 업스트림 변경과 Unsloth 패치가 함께 있고, 어느 쪽 때문에 빠른지는 확인하지 않았습니다.

설정 고르기

대표 설정은 소설로 한 번씩 몇 가지 조합을 재 보고 골랐습니다. 모든 조합을 다 본 것은 아니고, 아래 값은 모두 1회 측정이라 대표 통계에 넣지 않았습니다.

엔진 설정 32K prefill / 생성 128K prefill / 생성
업스트림 b12008 ubatch 2048 654.70 / 37.17 503.71 / 35.28
업스트림 b12008 ubatch 1024 601.89 / 37.63 453.75 / 36.60
업스트림 b12008 batch·ubatch 4096 648.68 / 36.10 506.05 / 35.39
업스트림 b12008 ubatch 2048, -tb 36 661.37 / 37.44 509.18 / 35.44
업스트림 b12008 ubatch 2048, Unsloth 권장 로딩 옵션 645.84 / 37.47 497.06 / 35.85
Unsloth b11985-mix ubatch 2048 (선택) 638.18 / 44.83 493.26 / 41.46

단위는 tok/s입니다. ubatch를 1024로 낮추면 prefill이 8~10% 느렸고, 4096은 2048과 같았습니다. CPU 스레드를 늘리거나(-tb 36) Unsloth가 권하는 로딩 옵션(-lm mmap -lzm on -ot per_layer_token_embd.weight=CPU)을 써도 차이가 없었습니다. 엔진만 Unsloth 빌드로 바꾸면 128K 생성이 35.28에서 41.46으로 올랐습니다.

결과

소설

문맥 추가 prefill (tok/s) 추가 prefill (초) 생성 (tok/s)
32K 666.39 49.17 44.20
64K 606.31 54.05 42.95
96K 557.27 58.81 42.68
128K 510.11 64.25 41.53

코드

문맥 추가 prefill (tok/s) 추가 prefill (초) 생성 (tok/s)
32K 706.36 46.41 44.43
64K 630.17 52.01 43.62
96K 575.16 57.02 42.57
128K 534.20 61.39 41.32

3회 평균입니다. 표준편차는 prefill이 1.71~19.80, 생성이 0.04~1.07 tok/s였습니다. 첫 토큰까지 걸린 시간은 단계마다 추가 prefill 시간과 0.01초 안에서 같았습니다(앞 단계 캐시를 재사용하므로 128K 전체를 처리하는 시간이 아닙니다).

생성은 32K에서 128K까지 44에서 41 tok/s로 조금 줄었습니다. prefill은 문맥이 32K씩 늘 때마다 처리 시간이 약 5초씩 늘었습니다(소설 49초에서 64초). 같은 기계에서 llama.cpp로 잰 Qwen3.8 UD-Q4_K_XL은 약 1.3초씩이었습니다.

DSpark 드래프터를 켜면

Unsloth는 DeepSeek V4.1용 DSpark 드래프터(Q8_0, 7.98GB)를 함께 냈습니다. 작은 모델이 다음 토큰 몇 개를 미리 짐작하고 본 모델이 한꺼번에 검증하는 speculative decoding입니다. Unsloth 권장값(--spec-type draft-dspark --spec-draft-n-max 3)으로 켜고 소설과 코드를 한 번씩 쟀습니다.

엔진 원문 32K prefill / 생성 128K prefill / 생성
업스트림 b12008 소설 637.61 / 29.55 485.16 / 28.63
업스트림 b12008 코드 687.30 / 73.79 514.82 / 60.96
Unsloth b11985-mix 소설 633.92 / 29.82 493.58 / 29.50
Unsloth b11985-mix 코드 695.27 / 73.47 512.07 / 60.41

코드는 빨라지고 소설은 느려졌습니다. Unsloth 빌드에서 128K 생성은 코드가 드래프터 없는 대표값의 1.46배, 소설이 0.71배였습니다. prefill은 거의 같았습니다. 코드 출력은 32K와 128K 모두 드래프터 없이 낸 것과 같은 토큰이었습니다. 소설은 드래프터 없이도 회차마다 출력이 달라(아래) 비교할 수 없었습니다.

공통 벤치마크의 대표 결과는 소설과 코드에 같은 설정을 써야 해서 드래프터를 끈 쪽을 대표로 했습니다. 코드 위주로 쓴다면 켜는 편이 빠릅니다. 드래프터를 켠 결과는 원문마다 1회씩만 쟀습니다. oMLX 글에서 모델 내장 MTP를 켰을 때도 코드는 빨라지고 소설은 대부분 느려졌습니다.

같은 기계의 다른 DeepSeek V4.1 결과 (참고)

같은 기계에서 잰 다른 두 결과의 128K 값을 적어 둡니다. 모델 파일과 측정 방식이 모두 달라서 공통 벤치마크 기준으로 나란히 놓는 비교는 아닙니다.

llama.cpp (이 글) ds4 oMLX
모델 파일 Unsloth UD-Q2_K_XL antirez Q2 mlx-community oQ2e-mtp
측정 방식 HTTP /completion (원래 방식) C 세션 API (변형) 엔진 직접 호출, 생성분 재처리 (변형)
128K 생성, 소설 / 코드 41.53 / 41.32 37.03 / 37.06 56.69 / 58.35
128K 추가 prefill, 소설 / 코드 510.11 / 534.20 1455.11 / 1590.56 747.61 / 808.95

생성은 ds4보다 조금 빠르고 oMLX보다 느렸으며, prefill은 셋 중 가장 낮았습니다. 세 값 모두 speculative decoding을 끈 것입니다.

관찰

  • 같은 설정, greedy인데 회차마다 출력이 달랐습니다. 소설은 32K부터, 코드는 64K부터 출력 토큰의 해시가 회차마다 다릅니다(코드 32K는 세 회차가 같았습니다). 같은 기계에서 Qwen3.8 UD-Q4_K_XL을 llama.cpp로 잰 결과도 같은 양상이었습니다. 원인은 확인하지 않았습니다.
  • 짧은 확인으로 업스트림 b12008에 이미지 입력용 투영 파일(mmproj)을 붙여 32K 문맥으로 띄웠습니다. "17 × 23"에는 생각 모드를 켜든 끄든 391로 답했고, 빨간 원과 파란 사각형이 있는 그림 질문에도 맞게 답했습니다. 짧은 문맥 생성은 42~45 tok/s였습니다.

측정 환경

측정은 10월 31일 새벽 약 26분 동안 했습니다. 메모리 여유는 18~19%였고, swap은 측정 전부터 있던 640~649MB에서 늘지 않았습니다. 측정하는 동안 같은 SSD에 다른 모델 다운로드가 초당 약 6~15MB씩 쓰고 있었습니다. 같은 기계에서 돌던 다른 추론 서버는 측정 전에 내렸습니다.

범위

출력 품질은 재지 않았습니다. Unsloth 빌드가 업스트림보다 생성이 빠른 이유, DSpark의 반복 측정, Unsloth가 256GB 기계에 권하는 더 큰 UD-IQ3_XXS 파일은 아직 재지 않았습니다. UD-IQ3_XXS는 회선 문제로 다운로드를 멈춘 상태입니다.