M5 Ultra 256GB에서 mlx-serve로 DeepSeek V4.1 Flash 1017, 128K 요청 한 번에 13~15초

M5 Ultra 256GB에서 DeepSeek V4.1 Flash 1017을 mlx-serve로 128K까지 쟀습니다. 128K 요청 한 번이 13~15초로 이 기계 엔진 중 가장 빨랐지만, 한국어 소설은 목표의 절반 길이로 써서 품질은 llama.cpp 쪽이 높았습니다.

Mac Studio M5 Ultra(GPU 80코어, 통합 메모리 256GB)에서 DeepSeek V4.1 Flash 1017을 ds4, oMLX, llama.cpp로 차례로 쟀고, 이번에는 Zig와 MLX로 만든 추론 서버 mlx-serve를 붙였습니다. 가중치는 세 가지입니다. oMLX 때와 같은 oQ2e-mtp, mlx-serve 작성자가 낸 전용 팩 iQ-MLX 3.0bpw, 커뮤니티 플러그인으로 돌리는 Sushi STQ 1.75bpw입니다.

작업은 앞의 글들과 같습니다. 소설과 코드 원문을 32K토큰씩 이어 붙여 128K까지 가며, 단계마다 새로 넣은 약 32K의 처리와 이어지는 256토큰 생성을 원문마다 3회 쟀습니다. 회차마다 서버를 새로 띄웠고, 세 팩 모두 기본 설정과 speculative decoding을 끈 설정에서 24단계가 전부 유효했습니다. 여기에 실제 일에 가까운 시험으로 한국어 단편 소설 한 편을 쓰게 해 걸린 시간과 결과물을 함께 봤습니다.

mlx-serve 팩 세 가지는 이 기계에서만 쟀습니다. 다른 기계에는 같은 파일로 잰 결과가 없습니다.

13~15초128K 위치 요청 한 번 (첫 토큰 + 256토큰 생성)
64.51 / 122.83 tok/s128K 생성, 소설 / 코드 (3.0bpw 팩 기본 설정)
약 128GBSushi 1.75bpw 팩의 상주 메모리, 속도는 3.0bpw와 거의 같음
62초 / 176초한국어 소설 한 편, mlx-serve / llama.cpp (3편 평균)

기계·엔진별 128K 위치 요청 한 번

128K까지 쌓인 문맥에 새 입력 약 33K를 넣고 256토큰을 받는 한 번의 요청입니다. 기계와 엔진마다 대표로 정한 구성의 값입니다. 합계는 첫 토큰까지의 시간에 256토큰 생성 시간을 더한 것입니다.

기계 · 엔진 가중치 · 설정 첫 토큰까지 (초) 128K 생성 (tok/s) 합계 (초) 측정 방식 회수
M5 Ultra · mlx-serve iQ-MLX 3.0bpw, DSpark+PLD (기본) 11.39 / 10.69 64.51 / 122.83 15.4 / 12.8 생성분 재처리 변형 3회 평균
M5 Ultra · mlx-serve Sushi STQ 1.75bpw, DSpark (기본) 11.94 / 10.45 62.72 / 124.13 16.0 / 12.5 생성분 재처리 변형 3회 평균
M5 Ultra · mlx-serve oQ2e-mtp, DSpark+PLD (기본) 11.98 / 11.27 65.55 / 111.15 15.9 / 13.6 생성분 재처리 변형 3회 평균
M5 Ultra · ds4 antirez Q2, 전부 상주, speculative 끔 22.52 / 20.60 37.03 / 37.06 29.4 / 27.5 C 세션 API 변형 3회 평균
M5 Ultra · oMLX oQ2e-mtp, MTP 끔 44.18 / 40.82 56.69 / 58.35 48.7 / 45.2 생성분 재처리 변형 3회 평균
M5 Ultra · llama.cpp Unsloth UD-Q2_K_XL, speculative 끔 64.25 / 61.39 41.53 / 41.32 70.4 / 67.6 원래 방식 3회 평균
M5 Ultra · llama.cpp Unsloth UD-IQ3_XXS, speculative 끔 73.64 / 62.69 40.67 / 40.24 79.9 / 69.1 원래 방식 3회 평균
M4 Max 128GB · ds4 antirez Q2, SSD 스트리밍 111.58 / 112.90 17.75 / 16.11 126.0 / 128.8 C 세션 API 변형 3회 평균
Ryzen AI Max+ 395 128GB 미측정 미측정

값은 모두 소설 / 코드 순서입니다.

같은 M5 Ultra 안에서 mlx-serve가 가장 빨랐습니다. 128K 위치에서 새 입력을 처리하는 속도가 초당 약 2,800~3,200토큰으로 ds4의 약 2배, llama.cpp의 약 6배였고, 생성도 speculative decoding을 끄고 비교해 llama.cpp의 약 1.6배였습니다. 기본 설정에서 코드는 DSpark 덕분에 초당 111~124토큰까지 나왔습니다. M4 Max의 ds4와 비교하면 한 번의 요청이 소설 기준 약 8분의 1 시간입니다. Ryzen AI Max+ 395(EVO-X2)에서는 1017 가중치를 아직 재지 않았습니다.

측정 방식은 엔진 구조 때문에 셋으로 갈리지만, 한 번의 요청이 하는 일(새 입력 약 33K 처리와 256토큰 생성)은 256토큰 차이 안에서 같아서 한 그래프에 놓았습니다. 원래 방식은 공통 벤치마크 그대로 HTTP로 토큰 ID를 보내고 앞 단계의 캐시를 정확히 이어 쓴 것입니다. mlx-serve와 oMLX는 앞 단계의 프롬프트 끝까지만 캐시를 이어 쓰고 앞 단계에서 생성한 256토큰을 다시 처리합니다(새 입력 32,769토큰 대신 33,025토큰이라 그만큼 불리합니다). ds4는 서버에 토큰 ID 요청 경로가 없어 C 세션 API로 쟀습니다. 모델 파일도 엔진마다 달라서 이 표는 각 엔진의 대표 구성을 나란히 둔 것이지 같은 파일의 엔진 비교가 아닙니다. 같은 파일끼리의 비교는 아래 oQ2e 절에 있습니다.

대표 설정

항목 값
기계 Mac Studio, M5 Ultra (CPU 12+24코어, GPU 80코어), 256GB, macOS 27.0.1
엔진 mlx-serve main 66c788b (26.11.1, MLX 0.32.3), 소스 빌드. 배포판 v26.10.3은 모델 카드에 128K prefill이 느리다고 적혀 있어 쓰지 않음
가중치 ddalcu/DeepSeek-V4.1-Flash-1017-MLX-Serve-iQ-MLX-3.0bpw v1.1 (rev 7fd908a1). 라우팅 전문가 3.00bpw(GPTQ), 어텐션·공유 전문가 MXFP8. 파일 47개의 SHA256을 HF 값과 모두 대조
메모리 배치 가중치 약 209GB 상주, Engram 표 두 층(203GB)은 SSD에서 필요한 행만 읽음. lm head는 불러올 때 8bit로 다시 양자화(엔진 기본, 손실 있음)
speculative 엔진 기본: DSpark(V4.1 자체 draft 단계, block 5) + PLD(n-gram이 안 맞으면 스스로 꺼짐)
KV 엔진 기본(양자화 끔)
문맥 135,168 고정, 동시 요청 1
전원·메모리 잠자기 방지, 저전력 모드 끔, GPU wired 한도 245,760MB
반복 회차마다 새 서버 프로세스, 짧은 인사로 워밍업. 로딩(25~31초)은 통계에서 뺌

lm head 8bit 재양자화는 끌 스위치가 환경 변수로만 있고 기본값이 켜짐이라 그대로 뒀습니다. 메모리가 빠듯하면 아래의 Sushi 1.75bpw 팩이 대안입니다. 같은 바이너리에 커뮤니티 플러그인 sushi_ds41을 붙여 돌리고, 속도는 거의 같은데 상주 메모리가 약 128GB입니다.

한국어 소설 한 편

속도만으로는 고를 수 없어서, 장문 시험 명세의 한국어 실용 과제 중 소설 하나를 다섯 구성으로 세 번씩(시드 42·43·44) 쓰게 했습니다. 목표 분량은 공백 빼고 9,000~11,000자이고, 온도 0.5, 사고 끔, 출력 상한 32,768토큰, 회차마다 새 서버입니다. 소설과 게임을 동시에 요청하는 과제와 고정 8K 과제는 아직 하지 않은 1단계입니다.

구성 한 편 시간 생성 (tok/s) 글자 수 (공백 제외) 품질 /24 (세 편) 반복 루프
llama.cpp + UD-IQ3_XXS, speculative 없음 176초 43.6 7,435 · 7,273 · 8,645 16 · 17 · 18 0/3
mlx-serve + 3.0bpw, DSpark+PLD (기본) 62초 75.8 3,357 · 5,385 · 5,562 10 · 15 · 10 0/3
mlx-serve + 3.0bpw, speculation 끔 98초 66.8 10,473(루프) · 4,686 · 5,009 7 · 13 · 12 1/3
mlx-serve + Sushi 1.75bpw, DSpark (기본) 73초 90.2 11,030(루프) · 4,381 · 5,033 5 · 8 · 16 1/3
mlx-serve + Sushi 1.75bpw, speculation 끔 74초 67.9 5,085 · 5,346 · 5,174 9 · 9 · 9 0/3

mlx-serve로 돌린 MLX 팩 두 종은 소설을 목표의 37~62% 길이로 쓰고 끝냈고 품질 점수도 낮았습니다. llama.cpp + UD-IQ3_XXS는 2.8배 오래 걸렸지만 가장 길게 쓰고 가장 높은 점수를 받았습니다. 엔진과 양자화가 함께 달라서 짧은 분량이 어느 쪽 탓인지는 아직 가르지 못했습니다.

빠른 쪽이 한 편을 62초에 끝낸 것은 반만 썼기 때문이기도 합니다. 15편 모두 분량 기준을 정상적으로 채우지 못했고, 범위 안에 든 2편은 같은 문장이나 문단을 되풀이해 채운 것입니다. 그 2편은 mlx-serve의 반복 차단이 끊었는데, API에는 finish_reason: "stop"으로 나가고 finish_details에만 repetition_loop가 붙습니다. 종료 이유만 보는 클라이언트에서는 자연스럽게 끝난 답과 구별되지 않습니다.

DSpark는 품질과 반복에 일관된 영향을 보이지 않았습니다. 루프는 켠 쪽과 끈 쪽에서 한 번씩 나왔고, 품질 평균 차이는 시드 사이 편차보다 작았습니다. 온도 0.5에서는 소설에서도 DSpark가 속도를 올려 Sushi는 1.33배, 3.0bpw는 1.13배였습니다.

품질은 요청 준수, 일관성, 전개와 결말, 가독성, 깊이, 반복 없음 여섯 항목을 0~4점으로 매겼습니다. 엔진 이름을 가린 파일을 Claude 모델 기반 평가자 다섯이 세 편씩 나눠 읽었고, 끝까지 읽었는지는 마지막 20자를 대조해 확인했습니다. 한 편을 평가자 한 명만 읽어서 평가자 사이 일치도는 재지 않았고, 구성마다 세 편이라 차이의 통계적 유의성은 주장하지 않습니다. 평가에서 MLX 팩 쪽은 짧은 분량, 소품과 시간의 모순, 같은 짧은 문장의 되풀이, 장면 대신 요약이 공통으로 지적됐습니다. 여섯 가지 고정 사실은 15편 중 14편이 지켰습니다.

EVO-X2의 Strata 시험에서도 소설이 문장 반복 뒤 중간에 끊겼습니다. 다른 모델과 다른 기계라 시간과 점수를 나란히 두지는 않습니다.

mlx-serve 세 가지 팩

팩 · 설정 128K prefill (tok/s) 128K 생성 (tok/s) 상주 메모리 측정 중 메모리 여유
3.0bpw, 기본 (대표) 2,900.01 / 3,087.93 64.51 / 122.83 약 209GB 9~13%
3.0bpw, speculation 끔 2,936.61 / 3,098.04 66.29 / 65.32 12~16%
Sushi 1.75bpw, 기본 2,767.08 / 3,161.26 62.72 / 124.13 약 128GB 45~48%
Sushi 1.75bpw, speculation 끔 2,782.26 / 3,170.39 66.78 / 65.94 45~48%
oQ2e-mtp, 기본 2,756.99 / 2,932.05 65.55 / 111.15 약 170GB 22~29%
oQ2e-mtp, speculation 끔 2,725.33 / 2,934.40 63.45 / 63.05 22~29%

소설 / 코드 순서, 3회 평균입니다. 생성 표준편차는 0.03~0.57 tok/s였고, 회차마다 DSpark 수락이 달랐던 oQ2e 기본 설정의 소설만 6.32였습니다.

비트 수가 더 많은 3.0bpw 팩이 oQ2e보다 빨랐습니다. speculation을 끄고 비교하면 생성이 1.04배, prefill이 1.06~1.08배입니다. 대신 상주 메모리가 39GB 더 크고 메모리 여유가 9~16%로 줄었습니다. Sushi 1.75bpw 팩은 3.0bpw와 속도가 거의 같으면서(speculation 끔 생성 1.01배, prefill 0.95~1.02배) 메모리를 절반 남짓만 써서 여유가 45~48%였습니다. Sushi 팩의 1.75bpw는 라우팅 전문가만의 평균이고, 어텐션과 공유 전문가는 4비트급입니다. 세 팩의 출력 정확도(KLD)는 재지 않았습니다.

문맥이 길어져도 생성 속도는 거의 그대로였습니다. 3.0bpw 팩 speculation 끔 소설이 32K 66.43에서 128K 66.29 tok/s였습니다.

DSpark는 글 종류에 따라 갈립니다

mlx-serve의 기본 설정은 V4.1에 들어 있는 DSpark draft 단계와 n-gram 기반 PLD를 켭니다. 128K에서 기본 설정을 speculation 끔과 비교하면 이렇습니다.

팩 코드 소설
3.0bpw 1.88배 0.97배
Sushi 1.75bpw 1.88배 0.94배
oQ2e-mtp 1.76배 1.03배

코드는 한 라운드에 제안 2~4개를 받아들여 128K에서도 초당 111~124토큰이 나왔습니다. 원문을 이어 쓰는 시험이라 코드는 반복되는 패턴이 많아 잘 맞습니다. greedy 소설은 제안이 대부분 거절됐습니다. 내장 경로는 수락이 낮으면 DSpark를 스스로 쉬게 해서 0.97~1.03배로 거의 같았고, Sushi 플러그인은 쉬지 않고 매 토큰 제안해 6% 느렸습니다.

대표는 소설 손해가 작고 코드 이득이 커서 기본 설정으로 했습니다. 다만 speculation을 켜면 greedy 출력이 회차마다 달라질 수 있습니다. 끈 설정은 소설과 코드 모두 3회 출력이 같았습니다. 3.0bpw 기본 설정에서는 소설 128K의 세 회차가 모두 서로 달랐고, 코드는 96K 단계만 speculation 끔과 달랐습니다. Sushi 플러그인은 기본 설정에서도 3회 출력이 같았지만, speculation 끔과는 소설 전 단계와 코드 96K에서 달랐습니다. 모델 카드는 greedy 출력이 순차 생성과 같다고 적었지만, 이번 측정에서는 확률이 거의 같은 후보 사이에서 갈렸습니다.

같은 oQ2e 파일에서 oMLX와 비교

oQ2e-mtp는 oMLX 글과 같은 파일이고 원문 토큰과 캐시 경계도 같습니다. 그래서 이 행끼리는 엔진만 다른 비교입니다.

128K, 소설 / 코드 prefill (tok/s) 생성 (tok/s)
mlx-serve, speculation 끔 2,725.33 / 2,934.40 63.45 / 63.05
oMLX 0.7.3.dev2, MTP 끔 747.61 / 808.95 56.69 / 58.35

mlx-serve가 prefill 약 3.6배, 생성 1.08~1.12배 빨랐습니다. 128K 지점 첫 토큰이 oMLX의 40~44초에서 11~12초가 됐습니다.

관찰

  • mlx-serve의 /tokenize는 HF 토크나이저와 결과가 다릅니다. 소설 원문을 HF는 417,059토큰, mlx-serve는 416,978토큰으로 자릅니다. 기호 하나 뒤에 ASCII 글자만 묶는 분할 규칙을 mlx-serve가 악센트 글자까지 묶어서, un'abilità처럼 아포스트로피 바로 뒤 단어에 악센트 글자가 있을 때 갈립니다. 이탈리아어·프랑스어 채팅 요청은 모델이 학습 때와 다른 조각으로 읽게 됩니다. 품질 영향은 재지 않았고, 측정에서는 HF로 자른 토큰 ID를 보냈습니다.
  • SSD에서 읽는 것은 Engram 표뿐입니다. oQ2e 소설 시험에서 서버가 실제로 디스크에서 읽은 양은 단계마다 prefill 중 약 16~17GB(초당 1.4~1.7GB), 생성 중 0.1~0.3GB였습니다. 이 SSD 대역폭의 10~12%와 1% 미만입니다.
  • oQ2e 측정 중에는 같은 SSD에 다른 팩 다운로드가 초당 약 15MB로 쓰고 있었습니다. 다운로드를 멈추고 한 번 더 재 보니 생성은 같고 prefill이 1~6% 높았습니다(1회). 3.0bpw와 Sushi 측정 때는 다른 부하가 없었습니다.

작성자 공개 수치와 비교

3.0bpw 팩의 모델 카드는 같은 기계 사양에서 33K 단일 프롬프트 prefill을 2,276 tok/s로 적었습니다. 이 측정에서 캐시 없이 32,768토큰을 처음 넣는 32K 단계는 기본 설정 3,468~3,550, speculation 끔 3,723~4,006 tok/s로 1.5~1.8배 높았습니다. 차이의 원인은 확인하지 않았습니다. 생성은 카드와 맞았습니다. 카드의 DSpark 수치(95.8~112.9 tok/s)는 이 측정의 코드 쪽에 가깝고, 카드도 산문에서는 DSpark가 물러나 66.7~72.4 tok/s라고 적었습니다.

작성자 쪽에서 10월 30일에 알린 prefill 3,879.9 tok/s는 약 1만 토큰 프롬프트를 캐시 없이 넣는 측정 도구의 값으로, 이 측정의 32K 단계와 같은 수준입니다. Sushi 플러그인 설명서의 32K prefill 3,079 tok/s도 이 측정(소설 2,902, 코드 3,190)과 맞았습니다.

측정 환경

속도 측정은 oQ2e와 3.0bpw가 10월 31일, Sushi가 11월 1일이고, 소설 시험은 11월 1일 새벽 약 30분 동안 했습니다. swap은 측정 중 늘지 않았습니다(576MB~1.08GB로 일정). 같은 기계에서 돌던 다른 추론 서버는 측정 전에 내렸다가 끝난 뒤 다시 띄웠습니다.

범위

세 팩과 GGUF의 출력 정확도는 재지 않았습니다. 소설 시험은 1단계로 소설 하나만 했고, 같은 llama.cpp로 UD-Q2_K_XL을 돌려 양자화만 바꾸는 시험과 ds4 Q2를 같은 조건으로 돌리는 시험이 다음 후보입니다. mlx-serve는 이 모델에서 동시 요청 묶음 처리를 지원하지 않아 소설과 게임을 동시에 받는 과제는 아직 하지 않았습니다.