본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 02. 09:39

ds4.c 해독하기 1/8

요약

Redis 제작자 antirez가 공개한 DeepSeek V4 Flash 전용 추론 엔진 DwarfStar(ds4)의 코드 분석 연재 제1회입니다. 범용 러너가 아닌 특정 모델에 최적화된 엔드투엔드 C 구현 설계 사상과 DeepSeek V4의 특이한 어텐션 구조를 다룹니다.

핵심 포인트

  • DeepSeek V4 Flash 전용의 엔드투엔드 추론 엔진 설계
  • MLA 기반의 3층 구조 어텐션으로 1M 토큰 컨텍스트 지원
  • KV 캐시를 디스크의 일급 시민으로 취급하는 설계 철학
  • 범용성 대신 특정 모델에 대한 좁고 깊은 최적화 선택

본 시리즈는 antirez(Salvatore Sanfilippo, Redis 제작자)가 공개한 DeepSeek V4 Flash 전용 추론 엔진인 DwarfStar(리포지토리명 ds4)의 코드를 해독하는 연재입니다. 제1회는 "왜 전용 엔진을 작성하는가"라는 설계 사상과, 이를 뒷받침하는 DeepSeek V4의 특이한 어텐션(Attention) 구조의 전체상을 다룹니다. 리포지토리: https://github.com/antirez/ds4

※ 코드의 행 번호 및 상수는 집필 시점(열람 커밋 ba00a8a) 기준입니다. 구현은 beta 품질(ds4-agent만 alpha)로 활발하게 변화하므로, 인용 부분은 각자 재확인하시기 바랍니다.

연재 「DwarfStar(ds4) 읽기」 전 8회

제1회 왜 전용 엔진을 작성하는가 (본 기사)

  • 제2회 비대칭 2bit 양자화(Quantization)와 imatrix

  • 제3회 Metal 그래프와 압축 KV

  • 제4회 디스크 KV 캐시

  • 제5회 서버와 DSML 도구 호출

  • 제6회 TCP 파이프라인 분산 추론

  • 제7회 네이티브 에이전트

  • 제8회 스티어링(Steering) · MTP · 평가 기반

  • DwarfStar는 범용 GGUF 러너가 아니다. DeepSeek V4 Flash(및 고메모리 기기용 Pro) 단 하나의 모델만을 로드, 프롬프트 정렬, 도구 호출, KV 영속화, HTTP API, 코딩 에이전트까지 엔드투엔드(End-to-End)로 "완성"하는 것을 목표로 한 C 구현이다.

  • GGML/llama.cpp에 링크하지 않는 완전 자기 완결형이다. 단, 양자화 레이아웃 및 커널에 대한 지식은 llama.cpp로부터 빌려왔으며, 경의를 표하기 위해 LICENSE에 저작권 표시를 남겨두었다.

  • DeepSeek V4의 어텐션은 MLA의 발전된 형태로, **raw sliding-window KV + 학습된 압축기(Compressor) + 희소 선택기(Indexer)**의 3층 구조를 가진다. 이것이 "1M 토큰 컨텍스트를 로컬 기기에서" 가능하게 한다.

  • 설계를 관통하는 한 문장:
    "KV 캐시는 RAM의 것이라는 발상을 버려라. 압축 KV와 고속 SSD의 시대, KV 캐시는 디스크의 일급 시민이다"

로컬 추론의 세계에는 뛰어난 프로젝트가 무수히 많지만, 새로운 모델이 나올 때마다 주목은 그쪽으로 옮겨갑니다. DwarfStar의 README는 이러한 상황에 대해 의도적으로 좁은 도박을 하겠다고 선언합니다.

This project takes a deliberately narrow bet: one model at a time, official-vector validation (logits obtained with the official implementation), long-context tests, and enough agent integration to know if it really works.

즉,

  • 한 번에 단 하나의 모델만을 상대한다
  • 공식 구현으로 얻은 logits(official vector)에 대해 정확성을 검증한다
  • 장문맥(Long-context) 테스트를 수행한다
  • "정말로 사용할 수 있는지" 확인할 수 있을 정도로 코딩 에이전트 통합까지 만들어낸다

범용 러너가 "넓고 얕게" 되기 쉬운 반면, DwarfStar는 "좁고 깊게"를 선택합니다. 저자의 비전은 명확하며, 로컬 추론이란 다음의 3가지가 상자에서 꺼내자마자 바로 맞물려 돌아가는 상태를 의미합니다.

A) HTTP API가 포함된 추론 엔진
B) 해당 엔진의 전제 조건에 맞춰 특별히 제작된 GGUF
C) 코딩 에이전트 구현을 통한 검증

이 A/B/C의 일체화된 사상이 이후 회차에서 다룰 "전용 GGUF 양자화", "DSML 도구 호출", "분산 KV 영속화"의 모든 전제가 됩니다.

또 하나, 커뮤니티 측면에서 솔직하고 흥미로운 점은, 이 코드가 GPT-5.5의 강력한 지원을 받아 작성되었다고 공언하고 있다는 점입니다.

버전 표기 "GPT-5.5"는 README 원문(strong assistance from GPT 5.5) 그대로입니다. 독자가 오기(誤記)로 오해하기 쉬운 부분이므로, 인용 시에는 원문 그대로임을 덧붙이는 것이 안전합니다.

이 소프트웨어는 GPT 5.5의 강력한 지원을 받아 개발되었습니다... 만약 AI가 개발한 코드가 마음에 들지 않는다면, 이 소프트웨어는 당신을 위한 것이 아닙니다.

「AI가 작성한 코드가 싫다면 쓰지 마라」라고 단언하면서도, 인간이 아이디어·테스트·디버깅을 주도했음을 명시합니다. AI 시대의 OSS(Open Source Software)가 나아가야 할 방향으로서도 읽어볼 만한 대목입니다.

엔진이 대상으로 하는 모델의 형상(shape)은 코드 내에 하드코딩(hard-coded)되어 있습니다. ds4.cDS4_SHAPE_FLASH를 보면 모델의 전체상을 한눈에 파악할 수 있습니다.

// ds4.c
static const ds4_shape DS4_SHAPE_FLASH = {
.name = "DeepSeek V4 Flash",
...

Pro 버전(DS4_SHAPE_PRO)은 61개 층(layer) / embd 7168 / 128개 헤드(head) / 384개 전문가(expert) / indexer top_k 1024로, 모든 수치가 한 단계 더 커집니다.

주목해야 할 점은 MoE(Mixture of Experts) 구조입니다. 256개의 라우티드 전문가(routed expert) 중, 1토큰당 6개만 활성화됩니다(n_expert_used = 6). 총 파라미터(parameter) 수는 거대하지만(Flash 기준 284B급), 토큰당 실효 계산량은 작습니다. 이것이 "284B 모델이 96/128GB 개인용 기기에서 동작함에도 불구하고, 27B/35B 규모의 dense 모델보다 훨씬 '크게 느껴진다'"라는 README의 주장에 대한 근거입니다.

파라미터 규모는 **DeepSeek 공식 모델 카드(HuggingFace, 2026-06-01 취득 시점)**를 바탕으로 합니다. 공식 문서에는 Flash를 284B / 액티브(active) 13B, Pro를 1.6T / 액티브(active) 49B, 컨텍스트(context) 1M, FP4+FP8 Mixed로 기재하고 있습니다. 모델 카드는 업데이트될 수 있으므로, 인용 시에는 취득일을 병기해야 나중에 차이를 추적할 수 있습니다. 본 기사의 n_expert_used 등은 ds4.c의 하드코딩된 형상으로부터 확인한 값입니다.

Being so large, Flash knows more things if you go sampling at the edge of knowledge.

DwarfStar의 최대 관전 포인트는 DeepSeek V4의 **압축 어텐션(compressed attention)**입니다. 일반적인 Transformer는 "모든 과거 토큰의 K/V를 유지하며 어텐드(attend)한다"이기 때문에, 컨텍스트가 길어지면 KV 캐시(KV cache)가 선형적으로 팽창합니다. DeepSeek V4는 이를 세 개의 층으로 나누어 해결합니다.

"최근 데이터는 고정밀 상태로 유지 · 오래된 것은 압축 · 압축된 행은 indexer로 간소화"하는 3단계 전략을 한눈에 알 수 있습니다. 이하, 각 층을 순서대로 살펴보겠습니다.

최근 DS4_N_SWA = 128 토큰의 K/V를 고정밀 상태 그대로 링 버퍼(ring buffer)에 유지합니다. ds4.ckv_cache_push_raw()는 버퍼가 가득 차면 한 줄씩 밀려나는 정직한 구현입니다.

// ds4.c: 최근 128 토큰의 raw KV. 가득 차면 한 줄씩 슬라이드
static void kv_cache_push_raw(ds4_layer_cache *cache, const float *kv) {
if (cache->n_raw < cache->cap_raw) {
...

128 토큰보다 오래된 정보는 버리는 것이 아니라, 압축하여 유지합니다. 이 부분이 핵심입니다. 압축률은 층마다 다르며, 층의 패리티(parity)에 따라 4와 128을 교대로 전환합니다.

// ds4.c: 층별 기대 압축률
static uint32_t ds4_expected_layer_compress_ratio(uint32_t il) {
switch (DS4_MODEL_VARIANT) {
...
  • ratio 0 (Flash의 첫 2개 층): 압축기(compressor) 없음. raw sliding window만 존재 = 순수한 국소 어텐션 (local attention).
  • ratio 4 (짝수 층): 4개의 토큰을 1개의 행으로 압축하는 가벼운 압축. indexer 포함.
  • ratio 128 (홀수 층): 128개의 토큰을 1개의 행으로 압축하는 매우 무거운 압축. indexer 없음 (행의 수가 적으므로 전부 확인할 수 있음).

"가벼운 압축으로 세밀하게 보는 층"과 "무거운 압축으로 대국적인 흐름을 보는 층"을 교차로 쌓음으로써, 국소적 정보와 대국적 정보를 모두 저비용으로 처리하도록 설계되었습니다. 층별 압축률을 나열해 보면, 짝수와 홀수에 따라 4와 128이 번갈아 나타나는 것을 알 수 있습니다.

층 : 0 1 2 3 4 5 6 ... 41 42
ratio : 0 0 4 128 4 128 4 ... 128 4
└ 국소적 정보만 │ └──────────────────────────┘
...

compressor는 단순한 평균 풀링 (average pooling)이 아닙니다. **스코어에 의한 softmax 가중 풀링 (softmax weighted pooling) + 위치 편향 (position bias)**이라는 학습된 메커니즘입니다. compressor_decode_one()의 본체를 따라가 보면,

// ds4.c: 1개 토큰 분량의 압축기 업데이트 (발췌)
// 1) 층 출력 x로부터 「KV 행」과 「스코어 행」을 동시에 투영 (projection)
matvec_q8_0_pair_prequant(kv_cur, sc_cur, model, wkv, wgate, xq, xscale);
...

compressor_pool_decode_state()의 내용은 스코어를 로짓 (logit)으로 간주한 softmax 가중 평균입니다.

// out[j] = Σ_r softmax(score[r][j]) * kv[r][j]
for (uint32_t r = 0; r < compress_ratio; r++) {
const float w = expf(state_score[r*width + j] - max_score);
...

즉, compressor는 "최근 N개 토큰 중 어떤 것을 얼마나 남길 것인가"를 **학습된 스코어로 가중치를 두어 1개의 행으로 압축하는 작은 어텐션 (attention)**입니다. 위치 편향 (APE)에 의해 N개 토큰 내의 상대적 위치도 고려됩니다.

ratio-4 층에서는 압축된 행이 대량으로 쌓입니다 (1M 컨텍스트라면 25만 행). 모든 행에 어텐드 (attend)하는 것은 무거우므로, indexer가 "현재 중요한 top-k 행"만을 선택합니다.

indexer는 본체 어텐션과는 독립된, 얇은 (head_dim 128) 쿼리 계통을 가집니다. 스코어 상위 n_indexer_top_k = 512 (Pro는 1024) 행만을 본체 어텐션에 전달함으로써, 계산량을 실질적으로 일정하게 유지합니다.

// ds4.c: indexer는 ratio==4인 층에만 할당됨
if (ratio == 4) {
cache->layer[il].index_comp_kv = xmalloc_zeroed(comp_cap * DS4_N_INDEXER_HEAD_DIM, ...);
...

이 3층 구조의 결과로, DeepSeek V4의 KV 캐시는 비정상적으로 작습니다. README의 수치에 따르면, 풀 1M 토큰 컨텍스트에서도 약 26GB이며, 그중 대부분(약 22GB)이 indexer입니다.

이 압축률이 효과를 발휘하면 세계관이 바뀝니다. 기존에는 "KV 캐시는 RAM에 두는 것, 컨텍스트를 다 쓰면 버리는 것"이었습니다. 하지만 압축된 KV와 현대의 고속 SSD를 결합하면, KV 캐시를 디스크에 영구화하여 세션을 넘나들며 재사용할 수 있습니다.

The KV cache is actually a first-class disk citizen.

제3회에서 다룰 Metal 그래프 상의 압축 KV, 제4회에서 다룰 DSV4 페이로드와 SHA1 디스크 KV 캐시, 제5회에서 다룰 서버의 프리픽스 (prefix) 재사용, 제7회에서 다룰 에이전트의 "세션 = 온디스크 (on-disk) KV"는 모두 이 한 문장에서 파생되었습니다.

성능 측면에서 DwarfStar가 DeepSeek V4 Flash를 추천하는 이유로는, thinking (사고) 섹션의 짧음이 꼽힙니다.

사고 (thinking) 모드에서 max thinking을 피할 경우, Flash는 사고 섹션을 생성하는데... 많은 경우 다른 모델의 1/5 수준에 불과하며, 결정적으로 사고 섹션의 길이는 문제의 복잡도에 비례합니다.

사고의 길이가 문제의 복잡도에 비례하기 때문에, 간단한 질문에는 짧게, 어려운 질문에는 길게 생각합니다. 이를 통해 다른 모델에서는 thinking을 활성화하면 실용적으로 사용하기 어려운 상황에서도, Flash는 thinking을 활성화한 채로 계속 사용할 수 있다는 주장입니다. 엔진 측도 DS4_THINK_NONE / HIGH / MAX의 3가지 모드를 가지며, 컨텍스트 길이(context length)에 따라 Think Max를 자동으로 폴백(fallback)시키도록 설계되어 있습니다 (ds4_think_mode_for_context()).

이 "1/5", "복잡도에 비례"라는 내용은 README 저자의 정성적인 관찰이며, 원문에는 비교 대상, 프롬프트 수, temperature, 컨텍스트 길이, 평균/중앙값과 같은 측정 조건이 명시되어 있지 않습니다. 본 기사에서도 "저자가 자신의 프로젝트에서 관찰한 주장"으로 읽어주시기 바랍니다. 정량적 비교가 필요하다면, 동일한 조건(동일 프롬프트 집합, 동일 샘플링, 동일 컨텍스트)에서 thinking 토큰 수를 직접 측정해야 합니다.

production 경로는 Apple Silicon의 Metal이 주 타겟이며, 다음으로 Linux의 CUDA(특히 DGX Spark / GB10 고려)를 지원합니다. CPU 경로는 "참조 및 디버깅용"이며, 심지어 macOS의 VM 구현 버그로 인해 커널 크래시(kernel crash)를 일으키므로 사용하지 말라고 강력히 경고되어 있습니다.

이 macOS 커널 크래시에 대한 기술은 README 저자의 경고를 그대로 인용한 것으로, 원문에는 대상 macOS 버전, 기종, 재현 조건, issue/radar 번호가 명시되어 있지 않습니다. 실질적인 피해가 클 수 있는 경고이므로, 실제로 CPU 경로를 시도하기 전에 최신 README와 issue를 확인하십시오. 프로덕션 환경은 Metal/CUDA를 사용하는 것을 전제로 하므로, 일반적인 이용 시에는 밟지 않게 되는 경로입니다.

make # macOS Metal
make cuda-spark # Linux CUDA, DGX Spark / GB10
make cuda-generic # 기타 CUDA GPU
...

주요 바이너리는 ds4 (CLI)와 ds4-server (HTTP API)이며, 추가로 ds4-agent / ds4-eval / ds4-bench가 있습니다.

제2회에서는 128GB Mac에서 284B 모델을 구동하는 핵심인 "비대칭 2bit 양자화 (asymmetric 2bit quantization)"와 "imatrix (중요도 행렬, importance matrix)"를 다룹니다. "라우티드 MoE (routed MoE) 전문가(expert)만 2bit로 낮추고 나머지는 건드리지 않는다"는 결단이, 어떻게 품질을 유지하면서 거대한 크기 감소를 실현하는지 살펴봅니다. gguf-tools/ 코드를 읽으며 IQ2_XXS / Q2_K의 블록 레이아웃(block layout)까지 깊이 파고들어 가겠습니다.

항목위치
모델 형상 정의ds4.c DS4_SHAPE_FLASH / DS4_SHAPE_PRO
레이어별 압축률ds4.c ds4_expected_layer_compress_ratio()
KV 캐시 확보ds4.c kv_cache_init()
압축기 본체ds4.c compressor_decode_one() / compressor_pool_decode_state()
설계 사상README.md "Motivations" 섹션

본 기사는 Quick Iterate의 로컬 LLM 연구의 일환으로, 공개 리포지토리 antirez/ds4의 코드를 분석한 것입니다. 행 번호, 상수, 벤치마크 값은 열람 시점의 커밋 ba00a8a (2026-05-30) / README 획득일 2026-06-01 기준입니다. ds4-agent는 alpha 단계이며, 엔진 본체는 beta 품질로 활발하게 변경되고 있으므로, 인용된 부분은 각자 최신 README 및 소스 코드를 통해 다시 확인하시기 바랍니다.

Quick Iterate Co., Ltd. ― IoT / 전력 모니터링 (Power Monitoring) / AI / 위성·무선 통신 (Satellite/Wireless Communication) / 시스템 통합 (System Integration)

로컬 LLM·에이전트 기반 (Agent Infrastructure)에 관한 문의는 언제든 환영합니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0