6배 작은 모델의 대가 — Qwen3.8-27B 4bit, Terminal-Bench 89개 1회차 35 대 45
Qwen3.8-27B MLX 4bit(17GB)를 Flash Next(100GB)와 같은 조건으로 Terminal-Bench 2.1 89개에 돌렸습니다. 1회차 35/89(39.3%) vs 원본 45·Uncensored 41. 갈린 태스크, 반복 루프 15건, 속도, 운영 메모

요약
9월 1일 글에서 Qwen3.8-27B 4bit을 “메모리가 빠듯하면 이걸 쓰라, 품질은 동등”이라고 썼습니다. 단문 프롬프트 몇 개로 본 결론이었습니다. Terminal-Bench 시리즈를 닫고 나니 같은 하네스에 이 모델을 넣어보지 않을 이유가 없었습니다.
ddalcu/Qwen3.8-27B-MLX-Serve-4bit (17GB, affine 4bit g64, MTP 헤드 내장)를 Flash Next 두 모델과 동일 조건(terminus-2, ctx 120K, temperature 0.3, reasoning xhigh, 타임아웃 1.0×)으로 89개 전부 한 번 돌렸습니다. 31시간 11분이 걸렸습니다.
결과: 35 / 89 (39.3%) — PASS 31, 타임아웃 시점에 파일이 맞아 통과한 PASS(TO) 4, FAIL 16, TO 38, ERR 0입니다.
| 1회차 (pass@1) | 통과 | 비율 | 소요 |
|---|---|---|---|
| Flash Next 원본 mixed-4-8bit (100GB) | 45 / 89 | 50.6% | 24.7h |
| Flash Next Uncensored 4bit (ARC4NUM) | 41 / 89 | 46.1% | — |
| 27B 4bit (17GB) | 35 / 89 | 39.3% | 31.2h |
참고로 Flash Next 두 모델의 pass@5는 64와 59입니다. 27B는 한 번만 돌렸으니 그 표와 직접 비교할 수는 없고, 이 글은 1회차 대 1회차만 봅니다. 다섯 번 돌릴지는 아래에서 말합니다.
한 줄로 줄이면 디스크 6분의 1, 정답 10개 손해, 그리고 속도 이득은 없었습니다. 손해 10개의 내용이 이 글의 본론입니다.
조건 한 가지 정정
이전 글들은 reasoning_effort=high로 썼고 이번엔 xhigh입니다. 다른 조건처럼 보이지만 실측해보니 mlx-serve 26.8.11은 Qwen3.8 템플릿이 모르는 high를 xhigh로 처리합니다(프롬프트 토큰 수가 53으로 동일). 즉 시리즈 내내 실제 조건은 xhigh였고, 이번 실행은 그것을 명시했을 뿐입니다. 동일 조건이 맞습니다.
두 모델의 구조도 적어둡니다. 둘 다 선형 어텐션 3 : 풀 어텐션 1의 하이브리드입니다. 27B는 64층 dense(풀 어텐션 16층), Flash Next는 48층 MoE(512 전문가 중 10개 활성, 풀 어텐션 12층 + 어텐션 인덱서)입니다. 활성 파라미터로는 27B가 Flash Next(~6B)의 4배쯤 되지만, 총 파라미터와 디스크는 6분의 1입니다.
어디서 갈렸나
세 모델 1회차를 겹쳐보면 이렇습니다.
| 구분 | 개수 | 태스크 |
|---|---|---|
| 셋 다 통과 | 24 | 짧은 것들 — fix-git, git-leak-recovery, openssl-selfsigned-cert, kv-store-grpc 등 |
| Flash Next 둘 다 통과, 27B만 실패 | 10 | bn-fit-modify, db-wal-recovery, distribution-search, fix-ocaml-gc, mailman, merge-diff-arc-agi-task, mteb-retrieve, polyglot-c-py, sqlite-with-gcov, torch-tensor-parallelism |
| Flash Next 둘 다 실패, 27B만 통과 | 6 | build-cython-ext, code-from-image, count-dataset-tokens, extract-elf, password-recovery, tune-mjcf |
| Flash Next 하나만 통과, 27B 통과 | 5 | filter-js-from-html, overfull-hbox, pytorch-model-recovery, reshard-c4-data, financial-document-processor |
| Flash Next 하나만 통과, 27B 실패 | 13 | caffe-cifar-10, crack-7z-hash, mcmc-sampling-stan, qemu-startup 등 |
| 세 모델 다 실패 | 31 | pass@5로도 못 푼 21개 + regex-log, torch-pipeline-parallelism, raman-fitting 등 |
격차 10 = (원본 45 − 27B 35)인데, 태스크 단위로 보면 27B가 원본에 없는 9개를 풀고 원본이 27B에 없는 19개를 풀었습니다. 1회차끼리의 비교는 이 정도로 흔들립니다. 시리즈에서 확인했듯 pass@1과 pass@5 사이 편차가 19개였습니다.
그래서 pass@5 결과와도 겹쳐봤습니다. 27B가 푼 35개 중 34개는 원본 pass@5 안에 있고, 유일한 예외가 filter-js-from-html(원본 5회 전패, Uncensored는 통과)입니다. Uncensored pass@5 기준으로는 extract-elf와 tune-mjcf 2개가 Uncensored 5회 전패였습니다. 두 Flash Next의 pass@5 합집합(68개) 바깥의 21개는 27B도 0개입니다. 27B가 새로 연 문은 없습니다. 다만 6분의 1 크기가 “큰 모델이 다섯 번 시도해야 풀리는 것”을 첫 시도에 몇 개 잡았다는 것은 적어둘 만합니다.
27B만 통과한 6개가 어떻게 풀렸는지 봅니다.
- tune-mjcf (14.2분, 30턴) — MuJoCo 케이블 시뮬레이션을 60% 시간 안에 같은 결과로 돌려야 하는 15분 태스크입니다. Flash Next 원본은 1회차 TO 뒤 2회차에 통과했고 Uncensored는 5회 전패였습니다. 27B는 8턴째 프로파일링으로 “솔버가 병목”임을 찾고, Newton → PGS를 돌려본 뒤
solver="PGS"한 줄로 2.18배(46%)를 만들었습니다. 한 번 만에 풀었는데 15분 제한에 48초 남았습니다. - extract-elf (PASS(TO), 15.0분, 35턴) — ELF에서 메모리 값을 뽑는 태스크입니다. 예시가 0x400000인데 바이너리의 .data는 0x4000이라 헤더를 30턴 동안 뒤지다가, 마지막 3턴에 “주소 체계를 여러 개 섞어 전부 출력”하는 쪽으로 돌아섰습니다. 타임아웃으로 끊겼지만 그 시점 파일이 채점을 통과했습니다. 정답을 이해한 것이 아니라 커버리지로 이겼습니다.
- code-from-image (11.5분) — PNG 속 의사코드를 읽어야 하는데 PIL도 tesseract도 없습니다. pip으로 easyocr을 깔고 줄 단위로 잘라 OCR,
SALT = b'0000TBENCH-SALT'를 복원해 sha256 체인을 계산했습니다. 힌트(“bee26a로 시작”)로 자기 답을 검증하고 끝냈습니다. - count-dataset-tokens (10.4분) — README가 401이라 다운로드된 캐시에서 찾아 읽고, “science 도메인 = biology·chemistry·physics 26개”로 좁혀 79,586을 냈습니다.
- password-recovery (5.9분) — .dat 파일 안에서 ZIP 로컬 헤더(PK\x03\x04)를 찾아
app/launchcode.txt를 꺼냈습니다. - build-cython-ext (PASS(TO)) — pyknotid를 NumPy 2.x에 맞게 고치는 태스크입니다. 24턴째 “18개 테스트 통과”였는데 이후
git diff가 pager에 걸려 4턴을 날리고 타임아웃됐습니다. 파일은 이미 맞았습니다.
여섯 개의 공통점은 없습니다. 굳이 찾자면 “탐색 경로가 한 번에 맞았다”는 것이고, 그것은 다섯 번 돌리면 큰 모델도 다 잡은 태스크들입니다.
38개 타임아웃 — 이번 주인공은 반복 루프
TO 38개를 제한 시간별로 보면 15분 13개 · 30분 13개 · 60분 8개 · 40분 2개 · 20분 1개 · 200분 1개입니다. 15분·30분짜리 26개는 Flash Next에서도 봤던 “환경 설치하다 끝남”이 많습니다(mcmc-sampling-stan은 30분 내내 RStan 컴파일, adaptive-rejection-sampler는 R 설치 후 11턴). 그것은 시리즈에서 충분히 썼습니다. 이번에 새로 본 것은 똑같은 턴이 수십 번 반복되는 루프입니다.
궤적에서 “직전 턴과 메시지가 완전히 같은” 연속 구간을 세어봤습니다. TO 38개 중 15개에서 10턴 이상 동일 반복이 나왔습니다.
| 태스크 | 반복 | 전체 턴 | 무슨 턴이었나 |
|---|---|---|---|
| regex-chess | 156 | 172 | “터미널이 멈춘 것 같다. Ctrl+C를 보내자” — 14턴째 ctx 21K에서 시작해 60분 내내 Ctrl+C |
| build-pov-ray | 143 | 168 | “SourceForge가 ’no’를 돌려준다. 다른 미러를 시도하자” — 같은 wget 143번, 200분 제한을 다 씀 |
| custom-memory-heap-crash | 64 | 141 | 아래 ② |
| extract-moves-from-video | 57 | 136 | grep -A 30 'FRAME 16' 같은 프레임 확인 명령을 57번 |
| protein-assembly | 41 | 62 | OAS 데이터베이스 접속 시도 41번 |
| break-filter-js-from-html | 34 | 40 | 6턴째부터 끝까지 같은 BeautifulSoup 테스트 스크립트 |
| polyglot-rust-c | 30 | 36 | “// #if 0은 안 된다, 다른 전략을 쓰자” 하면서 같은 파일을 30번 씀 |
| winning-avg-corewars · install-windows-3.11 · caffe-cifar-10 · path-tracing | 24~31 | ||
| distribution-search · polyglot-c-py · path-tracing-reverse | 10~17 | “계속 제자리를 돌고 있다(I’ve been going in circles)“고 쓰면서 같은 명령 |
regex-chess가 전형입니다. 14턴째 어떤 명령이 길게 걸리자 “터미널이 멈췄다”고 판단해 Ctrl+C를 보냈고, 그 관찰 결과(빈 프롬프트)를 보고 또 “멈췄다”고 판단했습니다. 관찰이 같으면 temperature 0.3에서 응답도 같고, 응답이 같으면 관찰도 같습니다. 324토큰짜리 턴이 156번, 60분입니다. build-pov-ray는 더 비쌉니다. 200분 제한 태스크라 3시간 20분이 wget 한 줄에 들어갔습니다.
Flash Next 시리즈에서도 반복 루프는 있었습니다. 하지만 467회 실행에서 눈에 띈 것은 대부분 130K 컨텍스트 벽에 부딪힌 뒤의 fallback 루프였지, ctx 20K에서 시작하는 순수 반복은 아니었습니다. 27B는 관찰이 조금이라도 달라질 여지가 없으면 빠져나오지 못합니다. polyglot-rust-c의 반복 메시지가 흥미롭습니다. “이 접근은 실패한다, 다른 전략이 필요하다”고 분석해놓고 같은 파일을 씁니다. 분석과 행동이 분리돼 있습니다.
같은 하네스에서 큰 모델이 덜 걸렸으니 모델 쪽 문제가 맞습니다. 하지만 하네스가 “직전 N턴과 명령이 같으면 개입”하는 것만으로 이 15개 중 대부분은 최소한 다른 시도라도 해봤을 것입니다. 시리즈 결론의 “반복 루프 감지 없음”이 작은 모델에서는 치명적이 됩니다.
② 130K 벽, 두 번. custom-memory-heap-crash(30분, 141턴)와 video-processing(60분, 747턴)입니다. 둘 다 컨텍스트가 130K에 닿은 뒤 서버 400(prompt 131,790 tokens exceeds ctx_size 131,072)을 받고, 하네스가 “Technical difficulties. Please continue”를 끼워 넣어 재시도하는 것을 남은 시간 내내 반복했습니다. video-processing 하나가 400 overflow 2,082건, 전체(2,223건)의 94%입니다. 시리즈에서 다섯 번째 본 패턴이라 새로울 것은 없고, 27B라고 다르지도 않았습니다.
16개 FAIL — “다 됐다”고 말하고 틀렸다
FAIL 16개 중 14개가 15분 안에 끝났습니다. 시간이 남는데 답이 틀린 유형이고, 마지막 턴은 거의 전부 “모든 요구사항이 검증됐다”로 끝납니다. 채점기가 무엇이라고 했는지 몇 개만 봅니다.
- mteb-retrieve (3.1분, 8턴) — 코사인 유사도 5위 문서를 내라는 태스크입니다.
HumanEval: Benchmarking Python code generation…을 냈고 정답은MTEB: Massive Text Embedding Benchmark입니다. 8턴 만에 자신 있게 제출했는데 순위가 어긋났습니다. - sqlite-with-gcov (5.6분) — 마지막 턴에 “sqlite3가 PATH에 있다”고 썼습니다. 채점기는
sqlite3 not found in PATH. 자기 셸에서만 잡힌 PATH였습니다. - nginx-request-logging (3.5분) —
Status code missing in logs. log_format에$status를 넣었다고 26턴째 보고했는데, 실제 로그 줄에는 없었습니다. - db-wal-recovery (9.7분) — 11개 레코드를 복구했다고 했지만
Apple should have updated value 150 from WAL. WAL의 갱신을 적용하지 않고 원본 값(100)을 냈습니다. - torch-tensor-parallelism (14.5분) — world_size 1·2·4 테스트를 직접 돌려 통과시켰는데, 채점기는 bias 분할과 출력
allclose가 어긋난다고 했습니다. - mteb-leaderboard (30.8분, 63턴) — 원본은 1회차에, Uncensored는 5회차에야 푼 태스크입니다.
codefuse-ai/F2LLM-v2-14B(0.7110)를 냈고 정답은GritLM/GritLM-7B입니다. Uncensored가 네 번 그랬듯 “28개 태스크 전부 있는 모델”로 좁히는 조건을 못 잡았습니다. - configure-git-webserver (1.8분) — 10턴, “end-to-end 테스트 통과”. 채점기는 404입니다. 테스트 데이터를 지우면서 사이트도 지웠습니다.
- merge-diff-arc-agi-task, mailman, bn-fit-modify, dna-insert, sparql-university, raman-fitting, train-fasttext — 같은 패턴입니다. bn-fit-modify는 학습한 DAG 자체가 틀렸고, mailman은 76턴을 쓰고도 announce 메시지 카운트가 안 맞았습니다.
Flash Next에서도 이 유형은 있었습니다(sparql-university의 복수 국적, extract-elf의 0.00%). 차이는 빈도입니다. 27B 1회차 FAIL 16개 중 10개가 원본 1회차에서는 PASS였습니다. “검증했다”는 문장이 실제 검증과 같지 않은 비율이 작은 모델에서 올라갑니다.
하나 더, regex-log (15분 TO, 5턴)입니다. 턴 4와 5가 각각 5,300토큰짜리 응답이었습니다. 테스트 케이스를 잔뜩 넣은 파이썬 스크립트를 “직전 시도가 너무 길었다”고 하면서 또 길게 썼습니다. 5턴에 15분입니다. 출력 길이 제어도 작은 모델에서 먼저 흔들립니다.
속도 — 17GB가 100GB보다 빠르지 않았다
이것을 기대하고 돌린 사람이 많을 것입니다. 서버 요청 로그(mlx-serve 26.8.11, MTP, 8-bit KV, M5 Max 128GB) 프롬프트 길이별 중앙값, tok/s입니다.
| 프롬프트 | 원본 프리필 / 디코드 | Uncensored | 27B 4bit |
|---|---|---|---|
| < 8K | 162 / 73.3 | 234 / 57.8 | 206 / 53.9 |
| 8–32K | 350 / 65.5 | 391 / 61.9 | 508 / 56.4 |
| 32–64K | 378 / 50.8 | 324 / 53.0 | 407 / 41.1 |
| 64K+ | 173 / 53.3 | 248 / 44.0 | 301 / 33.3 |
| 100K+ | 256 / 27.0 |
짧은 프롬프트에서 27B 디코드는 원본의 74%, 64K 넘어가면 63%입니다. 프리필은 오히려 27B가 빠른 구간이 있습니다(8–64K). 디코드가 느린 이유는 단순합니다. 토큰당 활성 파라미터가 27B 대 ~6B입니다. MoE의 “실제로 도는 것은 6B”가 4bit dense 27B를 이깁니다. 긴 컨텍스트에서 격차가 벌어지는 것은 풀 어텐션 층이 16 대 12인 데다 Flash Next에는 어텐션 인덱서가 있어서로 보이는데, 이것은 구조 차이를 안 뜯어본 추정입니다.
에이전트 루프에서는 어떻게 되는지 봅니다. 27B 턴당 소요 중앙값 0.36분(원본 0.38, Uncensored 0.39), 턴당 출력 301토큰(345, 334)입니다. 같습니다. 27B와 Uncensored가 둘 다 통과한 27개의 소요 시간은 중앙값 3.0 대 2.8분, 27B가 빨랐던 것이 12개입니다. 시리즈 결론 그대로입니다. 에이전트 시간은 컨테이너 안 명령 실행과 프리필이 먹고, 디코드 차이는 묻힙니다. 작은 모델이 사서 얻는 것은 메모리뿐이고, 이 머신에선 그것이 필요 없었습니다.
운영 메모
- 원래 09-08 21:07에 시작했다가 23:49에 처음부터 다시 돌렸습니다. 첫 5개 태스크 중 디코드가 43 → 20 tok/s로 반토막 났는데 모델이 아니라 Finder 폭주(CPU 148%, 7.6GB, fileproviderd 87%)였습니다.
killall Finder로 즉시 정상이 됐습니다. 조건을 깨끗이 하려고 재시작했고, 이후 31시간 동안 Finder는 얌전했습니다. - 서버: 요청 4,870건, wedge 재시작 0회, 메모리 admission 거부 0건, RSS 17GB 고정입니다. Uncensored 5회차 글에서 권한 대로
--prefix-cache-mem 4GB로 시작했더니 100K 컨텍스트가 상주해도 hot-cache가 상한을 지켰고(퇴거 2,601회) 스왑은 늘지 않았습니다. 지난번 스왑 47GB 사고는 8GB 설정이 원인이 맞았습니다. - 400 overflow 2,223건, 위에 쓴 두 태스크가 전부입니다. 서버 부하는 없었습니다.
- 31.2시간은 원본 24.7시간보다 6.5시간 깁니다. 모델이 느려서가 아니라 TO가 38 대 29라서입니다. build-pov-ray 200분 하나가 그 차이의 절반입니다.
다섯 번 돌릴 것인가
pass@5 표에 27B를 올리려면 네 번 더 돌려야 합니다. 1회차 실패 54개(FAIL 16 + TO 38)만 재시도해도 회당 20시간 안팎, 나흘입니다. 시리즈에서 배운 대로 pass@1 → pass@5 증분은 +18~19였으니 27B도 50 언저리까지는 갈 것입니다. 하지만 위의 15개 반복 루프는 temperature 0.3에서 재시도해도 같은 관찰 → 같은 응답으로 다시 빠질 가능성이 높고, 그러면 증분은 그보다 작습니다.
지금 결정은 보류입니다. 돌린다면 그 전에 하네스에 반복 감지를 넣는 것이 먼저입니다. 같은 명령을 같은 관찰로 세 번 받으면 개입합니다. 그것이 없는 채로 네 번을 더 돌리는 것은 build-pov-ray에 13시간을 또 주는 일입니다.
한 줄 요약: 27B 4bit은 1회차 35 대 45입니다. 새로 연 태스크는 없고, 15분 안에 “검증했다”고 틀리는 FAIL과 ctx 20K에서 시작하는 순수 반복 루프가 격차를 만들었으며, 디스크 6분의 1의 대가로 얻은 속도 이득은 이 머신에선 0입니다.