M5 Ultra에서 DeepSeek V4.1 Flash로 HTML 슈팅 게임 한 편, mlx-serve 51초·llama.cpp 180초
M5 Ultra 256GB에서 DeepSeek V4.1 Flash 1017에게 한국어 종스크롤 슈팅 게임을 HTML 한 파일로 쓰게 했습니다. mlx-serve가 한 편 평균 51초로 가장 빨랐지만 결함이 섞였고, 180초 걸린 llama.cpp가 가장 안정적이었습니다.
Mac Studio M5 Ultra(GPU 80코어, 통합 메모리 256GB)에서 DeepSeek V4.1 Flash 1017로 한국어 소설 한 편을 쓰게 한 시험에 이어, 같은 장문 시험 명세의 게임 과제를 돌렸습니다. 한국어 UI의 종스크롤 슈팅 게임을 CSS와 JavaScript까지 담은 index.html 한 파일로, 설명문이나 코드 펜스 없이 내놓으라는 요청입니다.
엔진과 가중치 구성 세 가지를 seed 42·43·44로 세 편씩 돌렸고, 같은 구성으로 소설과 게임을 동시에 요청하는 회차도 세 번씩 했습니다. 같은 날 낮에 ds4의 새 빌드와 abliterated 변형 모델을 더 돌렸습니다. 나온 게임은 헤드리스 브라우저로 실제 실행해 보고, 엔진 이름을 가린 채 두 평가자에게 매기게 했습니다. 같은 게임 과제를 같은 가중치로 돌린 다른 기기는 아직 없습니다.
게임 한 편에 걸리는 시간
요청부터 응답 끝까지의 시간입니다. 회차마다 서버를 새로 띄웠고, 모델 로딩과 짧은 워밍업은 들어 있지 않습니다.
| 구성 | 한 편 시간 (s42 · 43 · 44) | 평균 | 생성 토큰 (s42 · 43 · 44) | 생성 (tok/s) | HTML만 출력 | 기능 실패 |
|---|---|---|---|---|---|---|
| mlx-serve + iQ-MLX 3.0bpw, DSpark+PLD | 51.1 · 40.7 · 61.3초 | 51.0초 | 5,846 · 5,441 · 8,302 | 118.8~136.7 | 단독 2/3, 동시 2/3 | 동시 2편 |
ds4 + Q2, main 890898c (PR #1073) |
130.1 · 151.7 · 140.0초 | 140.6초 | 7,374 · 8,574 · 7,786 | 약 56 | 0/3 | 1편 |
| llama.cpp + abliterated Mix-256G (변형 모델) | 160.4 · 158.4 · 189.2초 | 169.3초 | 7,140 · 7,070 · 8,552 | 44.8~45.5 | 1/6 | 동시 1편 |
| llama.cpp + UD-IQ3_XXS | 171.2 · 197.9 · 171.2초 | 180.1초 | 7,376 · 8,568 · 7,413 | 43.5~43.6 | 6/6 | 없음 |
| ds4 + Q2, PR #1261·#1262 빌드 | 182.1 · 189.5 · 191.4초 | 187.7초 | 6,915 · 7,210 · 7,284 | 약 38 | 0/3 | 없음 |
mlx-serve는 DSpark(모델에 든 초안 단계로 여러 토큰을 미리 짐작하는 speculative decoding)가 코드에서 잘 맞아 초당 119~137토큰이 나왔고, llama.cpp보다 약 3.5배 빨랐습니다. 게임 코드도 조금 짧게 썼습니다. ds4는 PR #1073이 들어간 main에서 생성이 1.5배가 되어 llama.cpp보다 빨라졌습니다. 생성 속도는 서버가 준 값이고, ds4는 서버가 주지 않아 클라이언트에서 잰 값입니다. mlx-serve의 s43은 첫 회차가 측정 프로그램의 버그로 실패해 다시 돌린 회차입니다.
구성과 설정
| 구성 | 엔진 | 가중치 | 그 밖의 설정 |
|---|---|---|---|
| mlx-serve | main 66c788b (10월 31일, MLX 0.32.3), 소스 빌드 |
iQ-MLX 3.0bpw 팩 v1.1, 약 209GB 상주, Engram 표는 SSD에서 읽음 | 엔진 기본 DSpark + PLD |
ds4 main 890898c |
PR #1073이 병합된 main | antirez Q2 (IQ2_XXS/Q2_K imatrix) | 평소 쓰는 서버 구성과 같은 인자, context 393,216, speculative 없음 |
| ds4 PR 빌드 | main ee7e840 + PR #1261·#1262 |
위와 같음 | 위와 같음 |
| llama.cpp UD-IQ3_XXS | Unsloth 배포 빌드 b11985-mix | Unsloth UD-IQ3_XXS (Engram 제외 233.5GB) | -ngl 99 -fa on -b 2048 -ub 2048, speculative 없음, 동시 회차는 -np 2 |
| llama.cpp abliterated | 같은 빌드 (세 빌드를 llama-bench로 재서 가장 빠른 것) | orcarouter DeepSeek-V4.1-Flash-1017-abliterated-GGUF Mix-256G (322.58GiB) |
위와 같고 -ub 1024만 다름 |
공통 조건은 소설 시험과 같습니다. HTTP /v1/chat/completions 스트리밍, 온도 0.5, top_p 1, top_k 0, min_p 0, 반복 벌점 끔, 출력 상한 32,768토큰, 사고(reasoning) 끔입니다. 회차마다 서버를 새로 띄워 관계없는 짧은 입력으로 64토큰 워밍업을 한 뒤 본 요청을 보냈습니다. mlx-serve와 llama.cpp의 context는 135,168입니다. 기계는 macOS 27.2입니다. mlx-serve는 정식 v26.11.1보다 앞선 빌드지만 두 판의 디코드 MoE 코드는 같습니다.
게임 과제와 검사 방법
요청은 한국어 UI의 오프라인 단일 index.html 종스크롤 슈팅입니다. 30초 일반전 뒤 2단계 이상의 보스전, 이동과 발사, 일반 적 3종, 탄환과 충돌과 피격 뒤 무적, 점수·체력·회복 아이템, 일시정지, 승리·패배·재시작을 고정 요구로 넣었고, 외부 라이브러리와 네트워크, 생략된 코드는 허용하지 않습니다.
나온 HTML은 헤드리스 Chromium(960×720)에서 외부 네트워크를 막고 실행했습니다. 발사를 누른 채 좌우로 0.45초씩 움직이는 고정 입력으로 125초를 돌렸고, 보충 검사로 3초째에 일시정지를 5초 걸었다 풀어 보고, 게임 오버 뒤 "다시 시작" 버튼을 눌러 봤습니다. 코드 펜스로 감싼 출력은 첫 펜스 안쪽만 기계적으로 뽑아 실행했습니다. 출력 코드는 고치지 않았습니다.
품질은 두 평가자가 따로 매겼습니다. 하나는 Claude Opus 5.5 기반 평가자 다섯 명이 세 편씩 나눠 코드 파일과 스크린샷을 직접 읽은 것이고, 다른 하나는 OpenAI 모델 기반의 Codex(gpt-6.1-sol, 추론 강도 xhigh)가 한 번에 한 편씩 코드와 실행 기록, 스크린샷 19장을 받아 매긴 것입니다. 항목은 요청 준수, 기능 정확성, 상태·타이밍 일관성, 코드 가독성, 예외 처리, 사용성 여섯 가지를 0~4점으로, 24점 만점입니다. 기능 일곱 가지는 화면으로 확인한 것만 통과로 치고, 화면으로 못 본 것은 미확인으로 두게 했습니다. 끝까지 읽었는지는 마지막 20자와 중간 구절을 적게 해 모두 확인했습니다. 두 평가자의 합계 점수 상관은 0.82, 평균 차는 0.00이었습니다.
결과
| 구성 | 첫 평가 /24 (단독 · 동시) | 두 번째 평가 /24 (단독 · 동시) |
|---|---|---|
| llama.cpp + UD-IQ3_XXS | 21.3 · 22.0 | 20.7 · 21.0 |
| mlx-serve + 3.0bpw, DSpark+PLD | 20.3 · 16.3 | 19.7 · 17.3 |
| ds4 + Q2, PR 빌드 | 18.0 (동시는 단독과 같은 출력) | 19.3 |
ds4 + Q2, main 890898c † |
19.3 | 20.0 |
| llama.cpp + abliterated † | 18.7 · 17.3 | 19.7 · 18.3 |
†는 같은 날 낮에 따로 매긴 묶음입니다. 그 묶음에 앞 원고를 섞어 다시 매기게 했더니 첫 평가자는 같은 원고를 평균 1.7점 짜게 줬고 두 번째 평가자는 +0.3점이라, 다른 구성과는 두 번째 평가로 비교하는 편이 맞습니다. 같은 묶음 안에서 PR 빌드 원고는 두 번째 평가 19.0이었습니다.
| 실행 검사 | llama.cpp (6편) | mlx-serve (6편) | ds4 PR 빌드 (3편) |
|---|---|---|---|
| 로드, 오류, 외부 요청 | 6편 정상 | 6편 정상 | 3편 정상 (펜스 안쪽) |
| 3초 일시정지 뒤 재개 | 6편 정상 | 5편 정상, 1편은 재개 뒤 멈춤 | 3편 정상 |
| 30초를 넘겨 보스 확인 | 3편 (30~31초 등장) | 2편 (30~31초 등장) | 0편 |
| 게임 오버 뒤 다시 시작 버튼 | 6편 새 판 | 5편 새 판 | 3편 새 판 |
| 승리 화면 | 0 | 0 | 0 |
llama.cpp UD-IQ3_XXS가 가장 안정적이었습니다. 6편 모두 HTML만 냈고 두 평가자 누구도 기능 실패를 짚지 않았습니다. 짚힌 것은 일시정지 중에도 배경이 흐르는 것, 같은 프레임에서 승리와 패배가 덮어쓰이는 것 같은 작은 결함이었습니다.
mlx-serve는 가장 빨랐지만 결함이 섞였습니다. 6편 중 2편을 코드 펜스로 감쌌고, 동시 회차 2편에 큰 결함이 있었습니다.
- 동시 s44: 640×800 좌표로 그린 화면을 축소하지 않아 점수와 체력 표시가 화면 밖으로 나가고 기체가 잘렸습니다. 두 평가자 모두 실패를 줬습니다.
- 동시 s43: 일시정지를 풀면 게임 루프가 다시 돌지 않아 그대로 멈춥니다. 두 평가자가 코드에서 짚었고, 실행 검사에서도 재개 뒤 100초 동안 화면이 바뀌지 않았습니다.
- 그 밖에 일반 적이 탄을 쏘지 않거나, 피격 직후 몸 충돌 피해가 무적을 우회하거나, 적 두 종류의 움직임이 거의 같은 곳이 있었습니다.
세 편씩이라 이 결함이 동시 처리 때문인지는 가를 수 없습니다. mlx-serve는 같은 seed로도 회차마다 출력이 달라집니다.
ds4는 3편 모두 ```html 코드 펜스로 감싸, "설명문과 코드 펜스 없이"라는 지시 중 펜스 부분을 매번 어겼습니다. 펜스 밖 설명문은 없었고 안쪽 코드는 동작했습니다. 일반 단계에서 죽어도 "보스에게 패배했습니다"가 뜨거나 보스 몸체와 부딪혀도 판정이 없는 결함이 짚혔습니다. PR #1073이 들어간 890898c는 1.5배 빠르면서 품질이 비슷했고 펜스도 그대로였습니다. 새 빌드의 s43은 일반 적 두 종류의 움직임이 같아 실패를 받았고, s44는 보스 2단계 진입 조건이 뒤집혀 보스 체력을 빨리 깎으면 2단계를 건너뛴다는 판독이 있었습니다(화면으로는 확인하지 못함).
abliterated 변형은 원본 UD-IQ3_XXS보다 조금 낮았습니다(두 번째 평가 단독 19.7 대 20.7, 동시 18.3 대 21.0). 6편 중 5편을 펜스로 감쌌고(원본은 0편), 동시 s42는 일반 적 두 종류의 움직임이 같아 두 평가자 모두 실패를 줬습니다. 원본 1017이 아닌 변형 모델이고 양자화도 달라서, 차이가 abliteration 때문인지 양자화 때문인지는 가를 수 없습니다.
승리 화면은 15편 어디에서도 보지 못했습니다. 고정 입력이 적의 탄을 피하지 않아 모든 게임이 125초 안에 게임 오버가 됐기 때문이고, 게임이 승리를 구현하지 못했다는 뜻은 아닙니다. 보스 패턴 전환, 승리, 승리 뒤 재시작은 대부분 미확인이고, 출력 코드를 고치지 않는 원칙 때문에 무적 같은 개입도 하지 않았습니다.
소설과 게임을 동시에 요청하면
같은 구성에 소설과 게임 요청을 100ms 안에 함께 보냈습니다. 두 답이 모두 끝날 때까지의 시간입니다.
| 구성 | 한 쌍 전체 시간 (s42 · 43 · 44) | 두 출력 합계 처리량 (tok/s) | 요청 하나의 생성 (tok/s) | 두 요청이 함께 생성된 시간 |
|---|---|---|---|---|
| mlx-serve + 3.0bpw, DSpark+PLD | 96.2 · 158.6 · 127.1초 | 86.1~92.9 | 43~58 | 80~133초 |
llama.cpp + abliterated, -np 2 |
238.7 · 206.5 · 207.0초 | 54.6~61.7 | 32~38 | 112~201초 |
llama.cpp + UD-IQ3_XXS, -np 2 |
305.0 · 227.4 · 262.0초 | 58.0~59.7 | 31~34 | 180~266초 |
| ds4 + Q2, PR 빌드 | 1,053.2 · 356.5 · 426.6초 | 37.7~38.1 | 약 38 | 0초 |
llama.cpp는 슬롯 두 개로 함께 생성해 합계 처리량이 단독(43.6)의 약 1.35배가 됐고, 요청 하나는 느려졌습니다. mlx-serve도 두 요청을 함께 생성했지만, 이 모델에서는 묶음 처리를 지원하지 않는다고 답하고 합계 처리량도 단독 소설과 단독 게임 사이에 머물렀습니다. ds4는 요청을 하나씩 처리합니다. 두 번째 요청은 첫 요청이 끝날 때까지 응답 머리도 받지 못했고, s42에서는 소설이 출력 한도 32,768토큰까지 반복하는 동안 게임 요청이 872.8초를 기다렸습니다. 그래서 ds4의 동시 회차 출력은 단독 회차와 바이트까지 같았습니다.
동시 처리로 소설 품질이 떨어졌다는 근거는 없었습니다(두 번째 평가 기준 llama.cpp 14.3 → 14.0, mlx-serve 12.0 → 13.0). 게임은 위에서 본 것처럼 mlx-serve의 동시 회차에서 큰 결함 2편이 나왔습니다.
범위
- 구성과 과제마다 세 편이라 차이의 통계적 유의성은 주장하지 않습니다. 평가자 둘 다 LLM이고 사람 검토는 하지 않았습니다.
- 샘플링은 명세값(온도 0.5)이고 실사용 기본값과 다릅니다. 시간, 길이, 품질을 합친 종합 점수는 만들지 않았습니다.
- 지금 ds4의 대표 빌드인 main
b3f5018(ds4 글)로는 장문 시험을 하지 않았습니다. greedy 출력이890898c와 같아 결과가 이어질 가능성이 높지만, 온도 0.5 출력까지 같은지는 확인하지 않았습니다. - oMLX와 Sushi 1.75bpw 팩은 게임 과제를 돌리지 않았습니다.
- EVO-X2의 Strata 시험에서 같은 게임 지시문을 다른 모델로 돌린 적은 있지만, 형식과 문법만 확인했고 모델과 기계가 달라 나란히 두지 않습니다.