llama2.c가 "작은 모델을 한 파일 C로"였다면, 이건 "프론티어급 초거대 모델을 노트북에서"를 증명한 살아있는 교재다. README 한 편이 143KB(교과서 한 권 분량)에 이르고, 모든 수치가 실제 측정값이며 측정 방법까지 공개돼 있다.
FareedKhan-dev/kimi-k3-in-c · 라이선스 Apache-2.0 · 언어 순수 C99(엔진 코어 약 5,600줄·176KB) + Python 도구 · 버전 v0.1.0(첫 공개 2026-07-31) · 최신 커밋 2026-08-01 · 제작 Fareed Khan(@FareedKhan-dev) · 플랫폼 Linux x86-64 · TrendShift Daily #9)
kimi-k3-in-c는 "디스크에 1.56TB로 저장된 2.78조 파라미터 모델 Kimi K3를, GPU 없이 CPU 한 대와 RAM 8GB만으로 한 토큰씩 추론해 내는, 프레임워크 의존이 전혀 없는 순수 C99 추론 엔진"이다. 한마디로 "초거대 모델을 노트북에서 돌리는 법"을 코드로 증명한 교과서다. 새 모델을 학습시키는 것도, 서비스로 배포하는 것도 아니다 — 이미 공개된 프론티어급 모델을, 가진 메모리가 아무리 작아도 돌아가게 만드는 "실행(추론) 엔진" 그 자체가 목적이자 결과물이다.
도서관 지하 창고(디스크, 1.56TB)에 백과사전 전집이 통째로 꽂혀 있다고 하자. 이 전집을 다 읽으려고 전부 책상 위(RAM)에 올리려 하면 책상이 수백 개 필요하다(= 수백 GB RAM). 보통의 방식이다.
kimi-k3-in-c는 다르게 접근한다. 책상 위엔 작은 독서대 하나(8GB)만 둔다. 지금 이 순간 필요한 책 몇 권만 창고에서 꺼내 독서대에 올려 읽고, 다 읽으면 도로 넣는다(= 스트리밍). 게다가 책을 이미 압축된 소형판 그대로 읽지, 굳이 큰 판형으로 확대 복사하지 않는다(= MXFP4 직접 연산). 그래서 독서대가 작아도 전집 전체를 읽어낸다. 대신 창고를 계속 오가야 하니 느리다 — 그게 이 마법의 유일한 대가다. 독서대를 크게 주면(RAM을 늘리면) 오가는 횟수가 줄어 빨라질 뿐, 읽어내는 내용(출력)은 글자 하나까지 똑같다.
왜 이게 어려운 문제인지부터 감을 잡자. 숫자 세 개만 기억하면 된다.
1,000GB가 필요할 일을 8GB로 줄인 것 — 그 124배의 격차를 어떤 정확도 손실도 없이(!) 메운 방법을 설명하는 것이 이 레포의 전부다. README의 표현을 빌리면 "클러스터에서 노트북까지 내려오는 네 번의 결정, 그리고 맨 아래의 답은 맨 위의 답과 똑같다."
첫째, 헤드라인 숫자 자체가 강렬하다. "2.78조 파라미터. CPU 한 대. RAM 8GB." — 이 한 줄이 개발자 커뮤니티를 흔들었다. 카파시의 llama2.c가 "작은 모델을 순수 C 한 파일로"라는 낭만으로 별 수만 개를 모았다면, 이 레포는 그 낭만을 프론티어급(2.78조) 규모로 밀어붙였다. "큰 모델은 큰 하드웨어가 필요하다"는 상식에 정면으로 반례를 든 셈이다.
둘째, "메모리를 다이얼로"라는 발상이 깔끔하고 새롭다. 대부분의 경량화는 모델을 손상시켜(양자화·가지치기) 억지로 우겨넣는다. 이 엔진은 반대다 — 모델은 손대지 않고, "어디에 바이트를 둘 것인가(RAM vs 디스크)"만 조절한다. 그래서 8GB든 224GB든 출력이 바이트 단위로 동일하다. 정확도를 파는 게 아니라 시간을 파는 트레이드오프라는 점이 우아하다.
사진을 줄이는 두 가지 방법. 4K 사진을 작은 폰에 담는 흔한 방법은 화질을 떨어뜨려(압축) 용량을 줄이는 것이다 — 화면엔 들어가지만 원본과 달라진다(=양자화 경량화). 다른 방법은, 원본 화질은 그대로 두고 지금 화면에 보이는 부분만 클라우드에서 조금씩 불러오는 것이다(=스트리밍). 화면이 작아도 원본을 손실 없이 본다. 느릴 뿐이다. kimi-k3-in-c는 두 번째 방식을 택했다. 모델을 흐리게 만들지 않고, 필요한 조각만 그때그때 디스크에서 불러온다.
셋째, 모든 수치가 "측정값"이고 측정 방법까지 공개된다. README에 등장하는 8.24GB·32.69초/토큰 같은 숫자는 어림값이 아니라 docs/data/ 폴더에 실측 로그(하드웨어 사양·저장장치 대역폭·메모리 사다리)까지 함께 박제돼 있다. 심지어 "이 수치는 재프리필(re-prefill) 때문에 부풀려졌으니 실제 디코드에선 더 낮을 것"이라고 자기 숫자의 한계까지 스스로 밝힌다. 과장 마케팅이 넘치는 AI 판에서 이 정직함이 신뢰를 만든다.
넷째, 가중치 없이도 정확성을 검증할 수 있다. 1.56TB를 내려받지 않고도 make test 한 줄이면 1분 만에 "이 엔진이 PyTorch 원본과 토큰 단위로 일치하는가"를 확인할 수 있다(작은 13층 오라클 모델로). 이건 교육 자료로서 결정적인 강점이다 — 배우는 사람이 거대 파일 없이도 코드의 정확성을 스스로 재현할 수 있으니까.
보통 화려한 벤치마크 숫자를 내세우는 레포는 문서가 얇거나 재현이 안 된다. 이 레포는 정반대다 — 143KB짜리 README가 한 컴포넌트씩 밑바닥부터 쌓아 올리는 강의이고, 모든 주장이 측정 로그로 뒷받침되며, "느리다·베이스 모델이라 대화가 안 된다·긴 프롬프트는 비현실적이다" 같은 약점을 먼저 큰 소리로 말한다. 화려한 카피 뒤에 견고한 엔지니어링과 정직함이 같이 있는, 흔치 않은 물건이다.
이 위업의 대가는 속도다. 8GB 예산에서 토큰 하나 뽑는 데 약 32초 걸린다(메모리를 224GB까지 늘려도 ~19초). 매 토큰마다 디스크에서 수십 GB를 읽어야 하기 때문이다. 게다가 베이스 모델이라 챗봇처럼 대답하지 않고 텍스트를 "이어 쓸" 뿐이고, 긴 프롬프트·청크 프리필·서빙 계층·비전은 아직 없다(그리디 디코딩만). 즉 "내 노트북에서 Kimi K3를 실무에 쓰자"가 아니라 "어떻게 이게 가능한지 배우자"가 이 레포의 올바른 용도다. Kimi K3 자체도 2026년 모델이라, 가중치는 Moonshot AI 배포처에서 직접 구해야 하고 이 레포엔 가중치가 전혀 없다.
이 프로젝트의 스택에서 가장 놀라운 건 거의 아무것도 안 쓴다는 점이다. 딥러닝 추론이라면 으레 딸려 오는 CUDA·cuDNN·BLAS(수치연산 라이브러리)·PyTorch가 전부 없다. 곱셈 커널마저 손으로 짰다. 이 "의존성 없음"이 176KB라는 크기와 "어디서나 컴파일된다"는 이식성의 근원이다.
| 영역 | 무엇을 쓰나 | 역할 |
|---|---|---|
| 언어 | C99 (표준 C, make portable는 제네릭 AVX2 타깃) | 엔진 전체. 외부 라이브러리 링크 0개 |
| 병렬화 | OpenMP (#pragma omp) | 행렬 곱을 코어 수만큼 나눠 실행. 이게 유일한 "가속" 수단 |
| SIMD | AVX2 + FMA (인트린식) | CPU 한 명령으로 여러 float를 동시 곱셈-누산. AVX-512는 불필요 |
| 디스크 I/O | O_DIRECT · pread · posix_memalign | OS 캐시를 건너뛴 직접 읽기. 스트리밍의 핵심 (리눅스 전용 이유) |
| 계측 | getrusage · clock_gettime | 피크 RSS(실사용 메모리)·구간별 벽시계 시간 측정 |
| vendored 3종 | json.h · tok.h · tok_unicode*.h | 미니 JSON 파서 + BPE 토크나이저(third_party/, Apache 라이선스) |
O_DIRECT · 왜 리눅스 전용인가O_DIRECT는 "캐시 쓰지 말고 디스크→내 버퍼로 곧장 복사하라"는 지시다. 흥미롭게도 이 하드웨어에선 O_DIRECT(3.2GB/s)가 버퍼드 읽기(2.3GB/s)보다 빨랐다 — 보통의 기대와 반대라, 저자도 "다른 장비에선 다시 확인하라"고 적어 뒀다. 이 시스템콜이 리눅스 고유라서 엔진이 Linux x86-64 전용이다.엔진 자체는 C지만, 주변 도구는 Python이다. 이들은 추론 실행에는 관여하지 않고(그래서 make test엔 Python이 필요 없다), 준비·검증·측정을 맡는다.
scripts/download-model.sh(체크포인트를 받아 공개된 총량과 바이트 단위로 대조: 부분 다운로드는 조용히 틀린 출력을 내니까), tools/pack_trunk.py(93개 층을 "한 층=한 번의 읽기"로 재배열).tools/make_k3_oracle.py·k3_ref.py·ref_forward.py: PyTorch로 참조 forward를 돌려 "정답"을 만들고, C 엔진 출력과 대조하는 픽스처를 굽는다.verify_mla.py·verify_kda.py·verify_expert.py·cmp_logits.py 등 각 커널을 개별 검증.tools/sim_cache.py(전문가 캐시 히트율을 임의 용량에서 재생), tools/budget.py(샤드 헤더에서 메모리 예산 계산).scripts/k3-doctor.sh: 이 머신이 모델을 돌릴 수 있는지, 어떤 프리셋이 맞는지, 저장장치가 얼마나 빠른지 보고.Makefile(GNU make) 또는 CMakeLists.txt. make -j면 C 파일 7개가 몇 초 만에 컴파일된다..github/workflows/ci.yml) — GCC·Clang 양쪽 빌드 매트릭스, 경고를 에러로(warnings-as-errors), ASan/UBSan(메모리·정의되지 않은 동작 탐지), Python·shell 린트까지.BLAS나 PyTorch를 끌어오면 코드는 짧아지지만, 그 안에서 무슨 일이 일어나는지 배울 수 없고 빌드도 무거워진다. 이 레포는 곱셈 커널까지 직접 써서, "행렬 곱이 실제로 어떻게 도는가"를 눈으로 읽을 수 있게 만들었다. 의존성 0은 제약이 아니라 설계 목표였던 셈 — 덕분에 어디서나 1분 안에 빌드되고, 모든 줄이 학습 대상이 된다.
모든 것의 출발점은 "2.78조 파라미터의 무게가 어디에 실리는가"를 세 덩어리로 가르는 것이다. 이 표가 곧 설계 전체다:
| 구성요소 | 크기 | 어디에 사나 |
|---|---|---|
| 라우팅 전문가 | 1.45 TB | 절대 RAM에 안 올림 · 디스크에서 스트리밍 · MXFP4(4비트) 그대로 |
| 덴스 트렁크 | 108.81 GB | "다이얼" — 상주시키거나 스트리밍하거나 (매 토큰 전체 재독) |
| 임베딩 + 최종 norm + lm_head | 4.70 GB | "바닥(floor)" — 항상 상주 |
| 순환 상태(93층) + KV 캐시 | ~5.3 GB + 위치당 2.37MB | 항상 상주 (KV 캐시는 --incremental일 때만) |
핵심은 이렇다. 1.45TB는 절대 RAM에 안 내려앉고, 108.81GB는 조절 가능한 다이얼이며, 약 5.3GB만이 반드시 있어야 하는 바닥이다. "8GB에서 돈다"는 말은 곧 "5.3GB 바닥 + 아주 작은 트렁크 창(window) + 작은 전문가 캐시"로 굴린다는 뜻이다.
이제 "1,000GB → 8GB"를 만든 네 번의 감축(four reductions)을 하나씩 본다. 이게 이 레포의 심장이다.
Kimi K3의 전문가 가중치는 애초에 MXFP4(4비트 부동소수) 형식으로 배포된다. 즉 파라미터 하나가 0.53125바이트밖에 안 된다. 보통의 추론 엔진은 이걸 계산 전에 fp32(4바이트)로 "펼쳐서(dequantise)" 쓴다. 이 엔진은 펼치지 않고, 압축된 4비트 니블(nibble) 그대로 곱한다(k3_matmul_mxfp4).
0, 0.5, 1, 1.5, 2, 3, 4, 6과 음수들)만 표현하되, 32개 값마다 공유하는 8비트 지수(스케일)를 곱해 범위를 넓힌다. 값 = E2M1[니블] × 2^(스케일−127). dequantise는 이 압축된 4비트를 계산용 32비트 실수로 "펼치는" 것. 이 엔진의 통찰은 "펼치지 마라"이다 — 펼치는 순간 데이터가 8배로 부푼다.전문가 하나는 33,030,144개 파라미터를 17,547,264바이트(17.5MB)에 담는다. 이걸 fp32로 펼치면 132MB가 된다. 그런데 한 토큰은 92개 라우팅 층 × 16명 = 1,472명의 전문가를 건드린다. 만약 매번 펼친다면 토큰 하나당 194GB어치의 펼치기가 필요하다 — 스트리밍 설계가 통째로 무너진다. "압축된 채로 곱한다"는 이 한 가지 결정이 전체 구조를 떠받친다.
93개 층 중 69개는 KDA(Kimi Delta Attention)다. 일반적인 트랜스포머 어텐션은 문장이 길어질수록 KV 캐시가 선형으로 커진다(위치마다 키·값을 저장). KDA는 대신 고정 크기의 "순환 상태 행렬" 하나에 과거를 압축해 담는다 — RNN처럼. 그래서 문장이 아무리 길어도 메모리가 안 커진다. 이게 스트리밍 예산을 예측 가능하게 만드는 두 번째 기둥이다.
나머지 24개 층은 Gated MLA다. 96개 어텐션 헤드가 각자 키·값을 들고 있으면 KV 캐시가 폭발한다. MLA는 키·값을 하나의 저차원 "잠재(latent)" 벡터로 압축해 캐시한다 — 위치당 2.37MB로 억누른다. MLA 층은 1-기반 위치 4, 8, 12, …, 92, 그리고 93에 놓여, 마지막 두 층이 모두 MLA다(끝 층은 항상 전역 어텐션을 한다).
여기가 제목의 그 문장이다. 층마다 반드시 쓰는 "덴스 트렁크"(108.81GB)를 전부 RAM에 올릴 필요가 없다. 고정된 층 순서대로 디스크에서 흘려 읽으면, "다음에 읽을 것"이 항상 미리 정해져 있어 디스크가 헛돌지 않는다(순차 읽기라 빠르다). 그래서 "트렁크 중 몇 층을 RAM에 고정(pin)할지"가 곧 메모리 다이얼이 된다 — 0층 고정(8GB)부터 90층 전부 고정(192GB+)까지.
src/io/k3_trunk.c의 링 버퍼(ring buffer) + 고정 접두부다: 앞쪽 N개 층은 RAM에 눌러 두고(pin), 나머지는 작은 순환 버퍼로 "읽고 → 쓰고 → 덮어쓰기"를 돌린다. 메모리를 늘리면 pin하는 층 수만 늘 뿐, 코드도 출력도 그대로다. "메모리 = 하한선"이 "메모리 = 성능 다이얼"로 바뀌는 지점이다.이 네 감축이 합쳐진 결과가 아래 "메모리 사다리"다. 실측값이며, 출력 토큰은 모든 줄에서 동일하다:
스트리밍하는 전문가에 LRU 캐시(src/cache/k3_cache.c, 배치 프리페치 포함)를 붙였다. 그런데 측정해 보니 흥미로운 사실이 나왔다 — 캐시가 8GB부터 64GB까지 히트율 36.24%로 완전히 평평했다. 메모리를 8배 늘려도 캐시 이득이 0이었던 것이다.
왜 LRU가 평평했나? Kimi K3의 라우터가 Quantile Balancing 기법으로 전문가 사용을 일부러 고르게 편다(특정 전문가 쏠림 방지). 그런데 "고른 사용"이야말로 LRU가 가장 못 견디는 패턴이다(재사용 지역성이 없으니까). 즉 약한 캐시 성능은 엔진의 결함이 아니라 모델 설계의 결과다. 게다가 미래를 아는 이상적 정책(Belady)은 같은 64GB에서 61.74%까지 올라가 "메모리를 사는 것보다 정책을 바꾸는 게 더 남는 방향"임을 데이터로 짚는다. "성능이 안 나올 때, 원인이 내 코드인지 문제의 본질인지 측정으로 가른다"는 태도의 교과서다.
k3.h 맨 위에 다섯 개의 불변식(invariants)이 못 박혀 있다. 이들의 공통점은 무섭다 — 어겨도 모델이 멈추지 않고, 유창한 텍스트를 뱉으며, 조용히 틀린다는 것이다. 초거대 모델을 밑바닥부터 재구현할 때 어디서 미끄러지는지를 압축한 목록이라, 그 자체로 배울 거리다:
| # | 불변식 | 어기면? |
|---|---|---|
| 1 | A_log는 헤드별로 인덱싱(채널별 아님). 체크포인트는 head_dim개를 담지만 앞 num_heads개만 유효, 나머진 패딩 | 디케이 게이트가 엉뚱한 값 → 조용히 다른 모델 |
| 2 | UT-transform 역행렬은 (I + Akk)^-1. 부호가 관례가 아니다 | 부호 하나로 recurrence 붕괴 |
| 3 | Aqk는 대각선을 유지, Akk는 버린다 | 둘을 같게 처리하면 미묘하게 오답 |
| 4 | MLA는 NoPE(위치인코딩 없음)지만, 64개 rope 차원은 여전히 존재하고 캐시된다. 회전만 없을 뿐 | rope 슬롯을 지우면 헤드 폭 192→128, 다른 모델 |
| 5 | MoE 라우팅 bias는 선택(selection)만 조종. 결합 가중치는 bias 없는 sigmoid 점수에서 | bias된 점수로 가중하면 같은 전문가 뽑아도 혼합비가 틀림 |
구조가 알려주는 힌트 셋. 첫째, 엔진 전체가 C 파일 7개·약 5,600줄뿐이다 — 75MB짜리 레포의 대부분은 문서 이미지와 기술 리포트 PDF지, 코드가 아니다. "작은 코어 + 두꺼운 문서·검증"이 이 프로젝트의 무게중심이다. 둘째, 파일 이름이 곧 파이프라인 단계다: tokenizer → bind → (trunk/cache로 가중치 조달) → core/ops(계산) → cli(루프). 셋째, docs/data/에 실측 로그가 코드와 함께 커밋돼 있다 — 주장과 증거가 같은 저장소에 산다.
계산(core/ops) · 가중치 조달(io·cache) · 모델 배선(model/bind) · 실행 루프(cli)가 깔끔히 갈려 있다. 특히 "가중치를 어디서 어떻게 가져오는가(트렁크 스트리밍·전문가 캐시)"가 계산 커널과 완전히 분리돼 있어서, 커널은 "가중치가 RAM에 있든 디스크에서 왔든" 신경 쓰지 않는다. 이 분리 덕에 "메모리 다이얼"이 커널을 건드리지 않고 io 층에서만 조절된다 — 큰 시스템을 층으로 자르는 법의 좋은 표본이다.
대부분은 "RAM에 안 들어가면 못 돌린다"고 생각한다. 이 레포는 "안 들어가면 흘려보내면 된다(stream)"는 발상의 전환을 실물로 보여 준다. 핵심은 계산 순서가 고정돼 있으면, 다음에 필요한 데이터를 미리 알 수 있고, 그러면 디스크에서 순차로 흘려 읽어도 느리지 않다는 것. 이 "예측 가능한 접근 순서 → 스트리밍" 패턴은 LLM뿐 아니라 대용량 데이터 처리·게임 에셋 로딩·DB 페이징 어디에나 이식된다.
압축된 4비트를 풀지 않고 그대로 곱하는 k3_matmul_mxfp4는, "데이터를 펼치는 순간 지는 게임"을 어떻게 피하는지 보여 준다. 저정밀 수 형식(fp4/int4)의 비트 레이아웃을 읽고 SIMD로 곱하는 커널을 뜯어 보면, 양자화된 모델을 효율적으로 돌리는 법의 밑바닥을 배운다. 니블 순서 같은 "관례일 뿐이지만 틀리면 조용히 망하는" 디테일도 함께.
KV 캐시가 안 커지는 KDA(선형 어텐션 + 망각 게이트)와, 키·값을 잠재 벡터로 누르는 MLA(NoPE)를 순수 C 구현으로 볼 수 있다. 논문으로만 접하던 최신 어텐션 변형들이 실제 코드로 어떻게 도는지, recurrence 순서(디케이→읽기→쓰기→출력)가 왜 "load-bearing"인지 손으로 만질 수 있다.
sigmoid 점수 계산 → bias로 "선택만" 조종 → 뽑힌 전문가를 bias 없는 점수로 가중 결합. 이 미묘한 순서가 왜 중요한지(#5 불변식)를 코드로 배운다. 라우터가 어떻게 "전문가 쏠림"을 막는지(Quantile Balancing), 그리고 그게 캐시 성능에 어떤 역설적 대가를 치르는지까지.
이게 어쩌면 LLM 지식보다 더 값진 배움이다. config 리더가 모르는 필드에 기본값을 안 넣고 로드를 거부하는 설계, 가중치 없이 도는 오라클 게이트(PyTorch 참조와 토큰 단위 대조), 각 불변식을 픽스처로 못 박기 — "정확성을 어떻게 테스트 가능하게 만드는가"의 교과서다. 크래시 안 나는 버그를 잡는 법을 배운다.
O_DIRECT로 OS 캐시를 우회하는 이유, posix_memalign 정렬 요구, getrusage로 피크 RSS 재기, 구간별 벽시계로 "시간이 어디서 새는지" 나누기. 시스템 프로그래밍의 실전 기법이 응축돼 있어, C로 성능 민감한 코드를 짜려는 사람에게 그대로 참고서가 된다.
1.56TB 없이도 이 레포의 정신을 맛볼 수 있다. git clone 후 make -j && make test만 돌려 보라 — 1분 안에 "엔진이 PyTorch 원본과 토큰 단위로 일치"하는 오라클 게이트가 통과하는 걸 볼 수 있다. 그다음 k3.h 맨 위 다섯 불변식을 읽고, src/core/k3_ops.c의 k3_matmul_mxfp4 하나만 정독해 보라. "4비트를 안 펼치고 곱한다"가 실제 코드로 어떻게 생겼는지 보이는 순간, 이 레포의 절반을 이해한 것이다.
| 항목 | 필요한 것 |
|---|---|
| OS | Linux, x86-64 — O_DIRECT·posix_memalign·getrusage를 쓰므로 리눅스 전용. macOS·Windows 미지원 |
| CPU | AVX2 + FMA(2013년 이후 대부분의 x86 CPU). AVX-512는 불필요. make portable은 제네릭 AVX2 타깃 |
| RAM | 8GB 이상 — 모든 프리셋이 동작. 메모리가 많을수록 빠를 뿐, 출력은 절대 안 변함 |
| 저장장치 | ~1.7TB 여유 공간(1.56TB 체크포인트 + 109GB 패킹된 트렁크). 빠른 로컬 디스크(NVMe)일수록 결정적 — 매 토큰 수십 GB를 읽으므로 |
| 툴체인 | GCC ≥ 9 또는 Clang ≥ 10, GNU make 또는 CMake |
| Python | 3.9+ — 다운로드·패킹·분석 도구용. make test엔 불필요 |
| 모델 가중치 | 레포에 없음. Kimi K3(2026, Moonshot AI)를 공식 배포처에서 직접 받아 해당 라이선스 준수 필요 |
헤드라인은 "RAM 8GB"지만, 진짜 병목은 저장장치다. 1.56TB짜리 체크포인트를 담을 공간이 있어야 하고, 그게 느린 HDD면 토큰 하나에 몇 분씩 걸린다. 실측 환경도 3.2TB NVMe(측정 대역폭 5~6GB/s)였다. 즉 "메모리는 작아도 되지만, 스토리지는 크고 빨라야 한다" — 병목이 RAM에서 디스크로 옮겨 갔을 뿐이다. 또한 이건 Linux x86-64 전용이라, 애플 실리콘 맥이나 윈도우에선 그대로 돌지 않는다.
| 목적 | 명령 |
|---|---|
| 빌드·검증 (가중치 불필요) | make -j && make test — 1분, 네트워크·모델 없이 오라클 게이트 통과 확인 |
| 내 머신 진단 | scripts/k3-doctor.sh — 어떤 프리셋이 맞는지, 저장장치가 얼마나 빠른지 보고 |
| 모델 받기·패킹 | scripts/download-model.sh(바이트 대조 검증) → tools/pack_trunk.py |
| 추론 실행 | ./bin/k3 ~/k3model --trunk ~/k3trunk --preset laptop --tok ~/k3model --prompt "..." --gen 8 --incremental |
| 메모리 프리셋 | --preset laptop|desktop|workstation|server|max (측정된 메모리 사다리에서 도출) |
git clone 후 make -j && make test. 1분 안에 "teacher forcing 32/32 · greedy 20/20 · incremental 20/20 · ENGINE MATCHES THE REFERENCE EXACTLY"가 뜨는 걸 확인하라. 그다음 일부러 커널 하나를 살짝 망가뜨려(예: k3_rmsnorm에서 eps를 빼 보기) 어떤 게이트가 어떻게 실패하는지 관찰하라. "정확성을 테스트로 못 박는다"는 게 무슨 뜻인지 몸으로 안다.
src/core/k3_ops.c의 k3_matmul_mxfp4와 k3_mxfp4_dequant를 읽어라. 그리고 종이에 니블 하나(예: 0b0110)와 스케일 바이트를 주고 값 = E2M1[니블] × 2^(스케일−127)을 직접 계산해 보라. "16가지 값만으로 어떻게 넓은 범위를 표현하는가", "왜 니블 순서를 뒤집으면 조용히 틀리는가"를 이해하면 저정밀 추론의 핵심을 잡은 것이다.
tools/sim_cache.py로 tests/fixtures/expert_trace.bin을 여러 용량에서 재생해, README가 말한 "8GB~64GB 히트율 36.24% 고정"을 직접 재현하라. 그다음 LRU 대신 다른 정책(예: 무작위 축출, 또는 Belady 근사)을 구현해 히트율이 어떻게 바뀌는지 실험하라. "메모리를 사느냐 정책을 바꾸느냐"를 데이터로 논증하는 경험이다.
작은 오라클 모델에서 불변식 #4(rope 슬롯 유지)나 #5(bias는 선택만)를 코드로 위반해 보라. 크래시가 안 나고, 텍스트가 여전히 유창하지만, 참조와 토큰이 어긋나는 "조용히 틀림"을 직접 목격하라. 이 경험이 "돌아가면 맞다"는 착각을 영구히 깨 준다. (실제 K3 가중치가 없어도 오라클 픽스처로 가능.)
src/io/k3_trunk.c의 링 버퍼 + 고정 접두부 아이디어를 완전히 다른 도메인에 적용해 보라 — 예컨대 "RAM에 다 안 들어가는 거대한 이미지/데이터셋을, 접근 순서가 고정된 파이프라인에서 스트리밍 처리"하는 미니 프로젝트. 핵심은 "예측 가능한 접근 순서 → 순차 스트리밍 → 메모리를 성능 다이얼로". 이 패턴을 손수 옮겨 보면 이 레포의 진짜 교훈이 LLM을 넘어선다는 걸 안다.
| 주차 | 주제 | 무엇을 · 어떻게 |
|---|---|---|
| 1주 | 트랜스포머 추론의 기초 | 카파시 llama2.c / llm.c를 먼저 읽어 "순수 C로 LLM 추론"의 골격(임베딩·어텐션·FFN·샘플링)을 잡는다. 그다음 이 레포의 cli/k3_run.c 생성 루프와 비교. |
| 2주 | 저정밀 수 형식 & 양자화 | fp16·bf16·int8·MXFP4/int4의 비트 레이아웃. OCP MX 표준 훑기 → k3_mxfp4_dequant / k3_matmul_mxfp4 정독. "펼치지 않고 곱하기"의 의미 체득. |
| 3주 | 최신 어텐션 변형 | 선형 어텐션(RWKV·Mamba 계열)과 KDA, 저차원 압축 MLA(DeepSeek 계열). k3_ops.c의 k3_kda_layer·k3_mla_cached를 논문과 대조. |
| 4주 | MoE와 라우팅 | top-k 게이팅·전문가 병렬·로드 밸런싱(Quantile Balancing). k3_router·k3_moe + docs의 캐시 분석으로 "고른 사용 vs 캐시 지역성"의 긴장 이해. |
| 5주 | 시스템 I/O & 스트리밍 | O_DIRECT·mmap·페이지 캐시·NVMe 대역폭·큐 깊이. k3_trunk.c(링 버퍼)·k3_cache.c(LRU+프리페치)·k3_st.c(safetensors) 정독. |
| 6주 | 정확성 검증 & 재현성 | 오라클 게이트·teacher forcing·픽스처 회귀·ASan/UBSan·"기본값 거부" config. 이 레포의 tests/·tools/verify_*.py를 자기 프로젝트에 이식하는 연습. |
| 용어 | 뜻 |
|---|---|
| MoE | Mixture of Experts. 층마다 수백 전문가 중 소수(K3: 896중 16)만 골라 쓰는 구조. 전체는 크지만 토큰당 활성 파라미터는 작다(~104B) |
| MXFP4 | 4비트 부동소수 형식(OCP MX FP4). 32개마다 공유 지수(E8M0)를 곱함. 파라미터당 0.53125바이트. K3 전문가가 이 형식으로 배포됨 |
| 트렁크 스트리밍 | 전문가를 뺀 층별 고정 가중치(108.81GB)를 RAM 상주 대신 고정 순서로 디스크에서 흘려 읽기. "메모리를 다이얼로" 만드는 핵심 |
| KDA | Kimi Delta Attention. 선형 어텐션 + 채널별 망각 게이트. KV 캐시가 안 커짐(고정 크기 순환 상태). K3 93층 중 69층 |
| MLA | (Gated) Multi-head Latent Attention. 키·값을 저차원 잠재로 압축 캐시(위치당 2.37MB). NoPE 사용. K3 24층 |
| NoPE | No Positional Encoding. 위치 회전을 안 함. 단 rope 차원(64) 슬롯은 존재·캐시됨(지우면 헤드 폭이 바뀜) — 불변식 #4 |
| SiTU-GLU | K3의 활성화 함수. tanh·sigmoid 조합에 β₁=4·β₂=25로 게이트 상한을 100으로 묶음 |
| safetensors | 텐서를 안전하게 저장하는 파일 포맷. 헤더(JSON) + 원시 바이트. k3_st.c가 의존성 없이 직접 파싱 |
| O_DIRECT | OS 페이지 캐시를 우회해 디스크→버퍼 직접 읽기. 매 토큰 재독하는 스트리밍에 최적(캐시가 헛수고라서) |
| 피크 RSS | Resident Set Size의 최대치 = 프로세스가 실제로 점유한 물리 메모리 정점. getrusage로 측정. "8.24GB"가 이 값 |
| 오라클 게이트 | PyTorch 참조 출력("정답")과 C 엔진 출력을 토큰 단위로 대조하는 테스트. teacher forcing·greedy·incremental 3종 |
| Quantile Balancing | 라우터가 전문가 사용을 고르게 펴는 학습 기법(tech report §2.3.3). 부작용으로 LRU 캐시 지역성을 떨어뜨림 |
| 조용히 틀림 | 크래시·에러 없이 겉보기 멀쩡한 결과를 내며 실제론 틀린 상태. LLM 재구현 최대의 적(유창해서 안 들킴) |
| 그리디 디코딩 | 매 스텝 가장 확률 높은 토큰(argmax)만 뽑는 방식. 이 엔진은 샘플링 없이 그리디만 지원 |
train-llm-from-scratch 등의 저자llama2.c·llm.c(순수 C LLM 추론의 원류) · llama.cpp(실용 CPU 추론) · sqliteai/waste(같은 K3를 NVMe 스트리밍으로 돌리는 자매격 프로젝트, 같은 트렌딩 목록 상위)