kimi-k3-in-c가 "초거대 모델을 노트북에서 돌리는 법"을 교보재로 증명했다면, waste는 그 아이디어를 제품 수준으로 끌어올린 판본이다 — 아무 프로그램에나 링크되는 임베더블 라이브러리 + 9개 명령의 CLI + OpenAI 호환 서버 + 비전(멀티모달) + 리눅스 cgroup 인식 예산기까지 갖췄다. "사람이 아이디어·가설·우선순위·테스트·결정을 내리고, 코드는 LLM(Opus 5)이 쓴다"는 개발 방식마저 README에 대놓고 적어 두었다.
sqliteai/waste · 라이선스 Apache-2.0(영구 오픈소스 선언) · 언어 C11(엔진 코어 약 10,600줄) + 변환용 Python · 버전 v0.6.3(2026-08-03) · 컨테이너 포맷 v0 · 저작권 SQLite Cloud, Inc. · 첫 공개 2026-07-28 · ★ 약 1,530 · 플랫폼 macOS · Linux · Windows(MinGW) · TrendShift Daily #16)
waste는 "디스크에 982GB로 저장된 2.78조 파라미터 모델 Kimi K3를, 대부분의 가중치를 RAM에 올리지 않고 디스크에서 스트리밍하며 추론해 내는, 의존성 없는 임베더블 C 추론 엔진"이다. 핵심 발상은 한 줄로 압축된다 — "모델의 무게가 RAM이 아니라 빠른 저장장치(NVMe SSD)에 살게 하고, 남는 RAM은 캐시로 쓴다." 새 모델을 학습시키지도, 모델을 손상시켜(양자화로 뭉개) 억지로 우겨넣지도 않는다. 이미 공개된 거대 모델을, 가진 RAM이 모델보다 훨씬 작아도 온전히 실행하는 "실행 엔진" 그 자체가 목적이자 결과물이다.
어떤 레스토랑의 냉장 창고(디스크, 982GB)엔 재료가 수천 종 있다. 흔한 방식은 모든 재료를 조리대(RAM) 위에 다 꺼내 두는 것이다 — 그러려면 조리대가 수백 개(수백 GB RAM) 필요하다.
waste는 다르게 한다. 조리대엔 모든 요리에 공통으로 쓰는 기본 재료(트렁크)만 상시 올려 둔다. 그리고 요리 하나(토큰 하나)마다 그 요리에만 필요한 희귀 재료(전문가) 몇 가지를 창고에서 그때그때 꺼내 온다(스트리밍). 방금 쓴 희귀 재료는 작은 보조 냉장고(전문가 캐시)에 잠깐 넣어 둬서, 또 필요하면 창고까지 안 가도 되게 한다.
여기 반전이 있다. 보조 냉장고를 지나치게 크게 만들면 오히려 요리가 느려진다 — 냉장고가 주방을 다 차지해 정작 조리할 공간이 없어지고(=페이징), "냉장고에서 꺼내기"가 "창고까지 다녀오기"보다 더 느려진다. 그래서 waste의 유일하면서도 가장 중요한 손잡이는 "보조 냉장고를 딱 알맞게 잡는 것"이다.
src/waste.h)로, 전역 상태가 없어 한 프로세스에서 여러 모델을 동시에 열 수 있고, 에러를 화면에 찍지 않고 반환하며, 절대 스스로 exit()하지 않는다. 즉 llama.cpp처럼 내 앱에 심어 쓸 수 있는 엔진이다.왜 이게 어려운 문제인지 숫자 몇 개로 감을 잡자.
수백 GB가 필요할 일을 29GB로 여는 것 — 그 격차를 정확도를 한 톨도 깎지 않고(출력이 캐시에서 왔든 디스크에서 왔든 비트 단위로 동일) 메운 방법을 설명하는 것이 이 레포의 전부다. 대가는 오직 하나, 속도다.
첫째, 헤드라인이 여전히 강렬하다. "2.78조 파라미터. 64GB 노트북. 온전한 모델." — 이 조합은 "큰 모델엔 큰 하드웨어가 필요하다"는 상식을 정면으로 반박한다. 게다가 waste는 그 위업을 장난감이 아니라 제품 뼈대 위에서 해낸다는 점이 다르다. 같은 아이디어의 교보재판(FareedKhan-dev/kimi-k3-in-c)이 "어떻게 가능한가"를 보여 줬다면, waste는 "이걸 실제로 쓸 수 있는 물건으로 만들면 무엇이 필요한가"에 답한다.
같은 원리, 다른 완성도. 라이트 형제의 첫 비행기와, 실제로 승객을 태우는 여객기는 "공기보다 무거운 물체를 날린다"는 원리는 같다. 하지만 여객기엔 좌석·계기판·정비 매뉴얼·안전 점검이 붙는다. kimi-k3-in-c가 "디스크 스트리밍으로 K3가 난다"를 증명한 첫 비행이라면, waste는 라이브러리 API·CLI·OpenAI 서버·비전·cgroup 안전장치·회귀 테스트를 붙여 "탈 수 있게" 만든 판본이다. 그래서 둘 다 트렌딩에 오른다 — 관심사가 다르니까.
둘째, "정확도가 아니라 시간을 판다"는 트레이드오프가 우아하다. 대부분의 경량화는 모델을 손상시켜(공격적 양자화·가지치기) 억지로 우겨넣는다. waste의 핵심 불변식은 정반대다 — 전문가가 RAM 캐시에서 왔든 디스크에서 갓 읽혔든, 출력은 비트 단위로 똑같다. 바이트를 "어디에 둘 것인가(RAM vs 디스크)"만 조절할 뿐, 계산 결과는 건드리지 않는다. 실제로 모든 층을 PyTorch 레퍼런스와 대조해 최종 로짓이 3.6e-06 이내, 비전 타워는 2.3e-06 이내로 일치함을 검증해 둔다.
셋째, "메모리를 더 주면 항상 빨라진다"는 직관을 데이터로 깨뜨린다. waste가 가르치는 가장 반직관적인 교훈은 "페이징 절벽(paging cliff)"이다. 전문가 캐시를 키우면 히트율은 계속 오르지만, 어느 선을 넘으면 처리량이 8배까지 폭락한다. 캐시가 커져 OS가 메모리 압박을 받으면 "캐시 히트"가 사실은 "디스크에서 다시 페이지를 불러오는 페이지 폴트"가 되기 때문이다. 이 현상을 4개 캐시 크기에서 한 프로세스로 측정해 표로 박아 뒀다(4장 참고). 이런 "실패 모드까지 정직하게 공개"가 신뢰를 만든다.
넷째, 가중치 없이도 정확성을 재현할 수 있다. 982GB를 안 받아도 make check 한 줄이면 작은 합성 컨테이너로 엔진 전체를 검증한다(변환용 Python도 필요 없다). 배우는 사람이 거대 파일 없이 코드의 정확성을 직접 확인할 수 있다는 건 교육 자료로서 결정적이다.
waste는 화려한 숫자만 던지지 않는다. 각 수치가 어떤 하드웨어·컨테이너·커밋에서 측정됐는지까지 명시하고, 불안정한 값은 범위로 적으며, 나중에 틀린 것으로 밝혀진 측정도 지우지 않고 기록으로 남긴다(docs/LEARNED.md). "실패한 아이디어와 부정적 결과를 일부러 보존한다"는 문화는, 최적화를 제안하기 전에 반드시 읽으라고 안내될 정도다. 위대한 카피 뒤에 견고한 엔지니어링과 정직함이 같이 있는, 흔치 않은 물건이다.
이 위업의 대가는 속도다. K3는 초당 0.45~0.62토큰 — 한 문장 뽑는 데 수십 초가 걸린다. 콜드 상태의 K3 토큰 하나는 디스크에서 약 17GB의 전문가를 읽어야 하고, 그래서 저장장치 속도가 전부다(내장 SSD 12.78GB/s vs 테스트한 USB 외장 0.94GB/s). 반드시 컨테이너를 내장 NVMe에 둬야 하며, USB 외장에 두면 실용 불가다. 또한 포맷·API가 아직 고정(freeze)되지 않았고, 릴리스 전 대규모 QA를 돌리지만 불안정성이 얼마든지 있을 수 있다고 스스로 밝힌다. "지금 당장 내 노트북에서 K3를 실무에"가 아니라 "로컬 추론을 저장장치로 어디까지 밀 수 있는지 탐구하는 프로젝트"가 올바른 이해다.
waste 스택의 첫 번째 놀라움은 추론 경로가 거의 아무것도 안 쓴다는 점이다. 딥러닝 추론이라면 으레 딸려 오는 CUDA·cuDNN·BLAS·PyTorch가 전부 없다. 행렬 곱 커널마저 손으로 짰다. Python·PyTorch·safetensors는 모델 변환과 검증에만 쓰이고, 추론에는 절대 관여하지 않는다. 이 "런타임 의존성 없음"이 이식성(macOS·Linux·Windows)과 임베더블성의 근원이다.
| 영역 | 무엇을 쓰나 | 역할 |
|---|---|---|
| 언어 | C11 (-std=gnu11, POSIX 확장) | 엔진 전체. 추론 경로는 libc + pthreads에만 의존 |
| 병렬화 | pthreads (OpenMP 아님) | 행렬 곱·리더 스레드. 전문가를 미리 읽어 오는 I/O 스레드도 여기 |
| SIMD | AVX2+FMA · AVX-512 · NEON (런타임 디스패치) | CPU 한 명령으로 float 여러 개 동시 곱셈-누산. 빌드시 3개 다 넣고 실행시 선택 |
| 디스크 I/O | pread · O_DIRECT(Linux) · F_NOCACHE(macOS) · FILE_FLAG_NO_BUFFERING(Win) | OS 페이지 캐시를 건너뛴 정렬 직접 읽기 — 스트리밍의 핵심 |
| 메모리 | posix_memalign · cgroup v2 (memory.max/high) 파싱 | 4KiB 정렬 버퍼. 컨테이너 안에서 실제 허용 RAM을 읽어 예산 산정 |
| 무결성 | CRC32 (crc32.c, 전문가 레코드 헤더) | 디스크에서 읽은 각 전문가 레코드의 손상 검증 |
| vendored | third_party/stb_image.h (비전 입력 디코딩) | 유일하게 끌어온 외부 헤더 1개(이미지 로딩). 나머지는 자체 구현 |
| 선택 백엔드 | metal.m (Metal 스텁), CUDA/BLAS 플래그 | 현재는 CPU+SIMD 경로가 가장 빠름. GPU 백엔드는 탐색 중 |
O_DIRECT / F_NOCACHE · 왜 일부러 OS 캐시를 끄나엔진은 C지만 주변 도구는 Python이다. 이들은 추론 실행에 관여하지 않고(그래서 make check엔 Python이 필요 없다) 준비·검증·측정을 맡는다.
tools/fetch_weights.sh(원본 가중치를 이어받기 가능하게 내려받고, --dry-run으로 필요 용량 사전 확인), tools/convert.py(torch+safetensors로 1.42TB 원본을 982GB WASTE 컨테이너로 변환 — 3워커 기준 약 4.7시간, 이어하기 지원).tools/kimi_ref.py·kda_ref.py·vision_ref.py·k3parts_ref.py: PyTorch로 참조 forward를 돌려 "정답"을 만들고 C 엔진 출력과 층별 대조. verify_container.py·fuzz_container.py는 컨테이너 파일을 검증·퍼징한다.routing_stats.py(전문가 캐시 히트율 시뮬레이션·트레이스 분석), memplan.py(메모리 예산 사전 추정), quant_lab.py·bitalloc_lab.py·mxfp4.py(양자화 실험), diskbench.c(저장장치 대역폭 측정).serve/) — python3 -m serve MODEL. 표준 라이브러리만으로, ctypes를 통해 같은 waste.h를 호출한다. 스트리밍·툴 호출·구조화 출력·thinking 제어·이미지를 지원(/v1/chat/completions).examples/) — api_text.c(텍스트 생성), api_plan.c(메모리 계획), api_vision.c(멀티모달)로 라이브러리 임베딩을 직접 보여 준다.Makefile(GNU make). make면 waste CLI + libwaste.a(정적)가, make libwaste.dylib/.so면 공유 라이브러리가 나온다. SIMD 파일별로 -mavx2/-mavx512f 같은 플래그를 override로 정확히 주입한다..github/workflows/ci.yml) — 다중 플랫폼 빌드, 모델 없는 테스트 스위트, 컨테이너 퍼저.waste는 로깅·시그널 핸들러·설정 파일·인자 파싱을 엔진에 절대 넣지 않는다 — 그건 호스트(=CLI)의 몫이다. "CLI가 어떤 기능을 필요로 하면, 먼저 waste.h에 넣는다"는 규칙 덕에, 사용자가 셸에서 할 수 있는 모든 일을 C 호스트도 할 수 있다. 결과적으로 엔진은 작고 순수하게 유지되고, 서버(Python)·CLI(C)라는 서로 다른 두 클라이언트가 같은 헤더 하나를 두드리게 된다. 관심사 분리가 이식성과 테스트 용이성으로 되돌아오는 좋은 본보기다.
모든 설계의 출발점은 "무게를 어디에 실을 것인가"를 세 덩어리로 가르는 것이다. 이 구분이 곧 아키텍처 전체다:
①은 반드시 RAM에 있어야 하는 하한선(floor), ②는 디스크에서 흘려보내는 대상, ③은 남는 RAM으로 사는 속도다. waste가 K3를 여는 데 필요한 29.06GB의 대부분은 사실 ③ 캐시가 아니라 ① 트렁크(27.28GB)다.
스트리밍이 빠르려면 디스크 배치(layout)가 전부다. WASTE 컨테이너(v0)는 단일 파일이 아니라 디렉토리이며, 네 가지 설계 목표로 짜였다:
pread로 읽는다. NVMe에서 "쓸 수 있는 대역폭 vs 못 쓰는 대역폭"의 차이가 여기서 갈린다.컨테이너 디렉토리는 이렇게 생겼다:
모델 파일이라고 무조건 믿지 않는다. cfg_sane()이 모든 차원 값을 상한으로 검사하고, 텐서 크기는 그것을 담은 파일 크기로 제한하며, format_version이 없거나 다르면 읽지 않고 거부한다. 전용 퍼저(fuzz_container.py)까지 둔다. 읽기 경로에서 각 전문가 레코드는 매직 넘버·기대한 전문가 번호·스트라이드·CRC32를 "믿는 게 아니라 매니페스트에서 유도해 대조"한다. 남이 준 모델 파일을 파싱하는 모든 프로그램이 새겨야 할 태도다.
"모델이 RAM에 안 들어가므로 전문가는 디스크에서 스트리밍되고, 한도가 걸린 캐시가 그게 얼마나 자주 읽기가 되는지 결정한다." — ecache.h의 첫 문장이 이 프로젝트 전체를 요약한다. 캐시 정책의 핵심 두 가지:
두 번째는 선행 읽기(read-ahead / 라우터 룩어헤드)다. 진짜 라우터가 다음 층의 전문가를 고르기 전에, 가벼운 예측기가 "다음 층에 필요할 전문가"를 미리 추측해 디스크 읽기를 먼저 시작한다. 계산과 I/O가 겹쳐 돌아가 대기 시간이 줄어든다. 중요한 건 진짜 라우터가 여전히 최종 결정을 내리므로, 이 예측은 타이밍만 바꾸고 결과는 바꾸지 않는다는 점(불변식 유지). 이 룩어헤드 덕에 3.32GB짜리 작은 캐시도 29.1% 히트를 내어, 17GB 캐시와 처리량 10% 이내로 붙는다.
waste의 예산기는 "메모리를 더 주면 빨라진다"는 순진한 가정을 거부한다. 사용자가 --budget을 안 주면 엔진이 스스로 안전한 값을 고르는데, 그 논리가 이 레포에서 가장 교육적이다.
전문가 캐시는 토큰 하나의 워킹셋(working set)의 정수배일 때만 의미가 있다 — 1배 미만이면 토큰 사이에 아무것도 못 남기고, 1배 위의 어중간한 초과분은 히트율 몇 점을 사는 대신 머신을 페이징으로 밀어 넣는다. 그래서 기본값은 워킹셋을 한 칸씩 밟아 floor + 3배, 안 되면 2배, 안 되면 1배, 그것도 안 되면 floor를 택한다. K3는 64GB 머신에서 floor + 1배(예산 46.25GB, 캐시 17.56GB)에 안착한다.
실측 표가 "왜 크다고 좋은 게 아닌가"를 못 박는다:
| 전문가 캐시 | 히트율 | 디코드 속도 | 해석 |
|---|---|---|---|
| 3.32 GB | 29.1% | 0.56~0.58 tok/s | 작아도 룩어헤드로 버틴다 |
| 17.32 GB | 36.2% | 0.63 tok/s | 측정 곡선의 정점 (기본값) |
| 23.32 GB | 38.4% | 0.07~0.09 tok/s | 히트율은 올랐는데 속도 폭락 ⚠ |
| 29.32 GB | 41.3% | 0.07~0.08 tok/s | 8배 느려짐 = 페이징 절벽 |
책상과 서류 더미. 책상(RAM)에 자주 보는 서류를 쌓아 두면 편하다. 그런데 서류를 책상이 넘치도록 쌓으면, 정작 펜을 놓고 글 쓸 자리가 없어져 서류를 바닥(디스크)에 잠깐 내렸다 다시 올리길 반복하게 된다. "책상에 있다"고 표시돼 있어도 실제론 바닥을 오가는 셈(=캐시 히트인 척하는 페이지 폴트). waste의 예산기는 "책상에 자리를 조금 비워 두는 게 오히려 빠르다"를 수치로 증명하고, 기본값으로 그 자리를 남긴다.
sysconf(_SC_PHYS_PAGES)는 호스트 전체 RAM을 보고한다 — 컨테이너에 32GiB만 허용됐는데도 256GiB로 착각한다. 그러면 예산기가 80GB를 달라고 하고, 커널이 프로세스를 즉시 죽인다(페이징 절벽과 달리 서서히 느려지는 게 아니라 그냥 kill). 0.6.3은 천장을 min(물리 RAM, cgroup 한도)로 바꿔, cgroup과 그 조상들의 가장 작은 memory.max/high를 읽어 이 문제를 없앴다. "환경이 말하는 용량을 곧이곧대로 믿지 말라"는 시스템 프로그래밍의 교훈.스트리밍이 통하려면 어텐션의 KV 캐시가 RAM을 잡아먹지 않아야 한다. 보통의 어텐션은 문맥이 길어질수록 KV 캐시가 선형으로 커져 RAM을 다 먹는다. K3는 KDA(선형 어텐션)를 주로 쓰는데, KDA 상태는 시퀀스 길이에 무관한 O(1)이다 — 헤드마다 행렬 상태 하나 + 작은 컨볼루션 창뿐. 그 결과 4K 문맥에서 KV 캐시가 11.25GB → 0.21GB로 줄어, 아낀 RAM이 고스란히 전문가 캐시로 간다(속도의 원천).
waste는 K3 출시 전에 공개된 Kimi-Linear-48B(같은 아키텍처 계열)의 레퍼런스 코드로 KDA 커널을 미리 구현·검증했다. 4개 층마다 하나꼴로 전통적 MLA(전체 어텐션) 층을 끼우는 3:1 인터리브 구조이며, MLA는 압축된 잠재(latent) KV로 문맥 비용을 더 낮춘다. K3에서 KDA 게이트 공식·출력 게이트가 Kimi-Linear와 미묘하게 다른데, 둘 다 "구조 차이가 아니라 설정으로 감지되는 차이"여서 커널 자체는 손댈 필요가 없었다.
waste의 어떤 부분도 모델의 수학을 바꾸지 않는다. 대신 바이트가 어디 사는가(RAM/디스크), 무엇을 언제 읽는가(캐시·룩어헤드), 얼마를 남겨 두는가(예산)를 재설계했다. 이건 데이터베이스·OS·게임 엔진이 수십 년간 써 온 메모리 계층(memory hierarchy) 최적화의 사고방식을, LLM 추론에 정면으로 적용한 사례다. "모델을 압축한다"만 떠올리던 사람에게 "배치를 바꾼다"라는 완전히 다른 축을 보여 준다.
읽는 순서를 추천하면: ①src/waste.h(무엇을 할 수 있는 엔진인지 = 계약서) → ②docs/ENGINE.md(왜 이렇게 나눴는지) → ③src/ecache.c(캐시 = 심장) → ④src/model.c(순전파 = 근육) 순이다. 10개가 넘는 docs/ 문서가 각 결정의 근거를 담고 있어, 코드보다 문서를 먼저 읽는 게 이 레포에선 오히려 빠르다.
docs/LEARNED.md · "부정적 결과의 박물관"pread·O_DIRECT·posix_memalign으로 OS 캐시를 우회한 정렬 직접 I/O를 어떻게 하는지, 왜 4KiB 정렬이 NVMe 대역폭을 좌우하는지, 리더 스레드로 I/O와 계산을 어떻게 겹치는지를 실코드로 배운다. cgroup v2 한도를 파싱해 컨테이너 안 실제 RAM을 읽는 부분은 클라우드/도커 환경 프로그래밍의 필수 교훈이다.
MoE 라우팅(top-16 선택), 잔차 벡터 양자화(3비트 전문가), 선형 어텐션(KDA)의 O(1) 상태, 압축 잠재 KV(MLA)를 프레임워크 없이 순수 C로 구현한 코드를 읽을 수 있다. "논문의 수식이 실제 커널에서 어떤 모양이 되는가"를 배우기에 이만한 교재가 드물다.
LFRU 축출, 표본 기반 희생자 선택, 워킹셋 정수배 예산, 그리고 "메모리를 더 줘도 느려지는" 페이징 절벽. 이건 운영체제·데이터베이스 수업에서 배우는 개념 그대로인데, 여기선 실측 표와 함께 살아 움직인다.
전역 상태 없음, 에러는 반환(출력 아님), exit() 금지, "CLI가 필요하면 헤더에 먼저"라는 규칙. 임베더블 C 라이브러리를 어떻게 설계하는가의 모범 답안이다. 같은 헤더를 C(CLI)와 Python(ctypes 서버)이 두드리는 것도 좋은 관찰 대상.
모든 수치에 하드웨어·커밋을 붙이고, 불안정한 값은 범위로, 틀린 값도 기록으로 남기며, 실패한 아이디어를 LEARNED.md에 박제한다. "측정이 핵심 산출물"이라는 태도는 코드 실력과 별개로 배울 값어치가 있다.
보통은 세 분야를 따로 공부하는데, waste는 세 개가 하나의 실제 문제(초거대 모델 로컬 추론)를 푸는 데 어떻게 맞물리는지를 보여 준다. 각 분야의 깊이도 얕지 않아, 어느 배경에서 와도 얻을 게 있다.
| 단계 | 필요한 것 | 비고 |
|---|---|---|
| 빌드 + 테스트 | C11 컴파일러 + make, macOS/Linux(Win은 MinGW) | make check는 합성 모델이라 가중치 다운로드 불필요 |
| 맛보기 (Kimi-Linear 48B) | RAM 1.28GB, 컨테이너 19GB | 약 10.65 tok/s — 엔진을 체감하기 좋은 진입점 |
| K3 풀 모델 | RAM 64GB 권장(하한 29.06GB), 내장 NVMe 약 1TB | 0.45~0.62 tok/s. 32GB로는 열리지만 심하게 페이징 |
| 직접 변환 시 | 추가 임시 저장 1.42TB(외장 가능, 이후 삭제) | 변환에 torch+safetensors, 3워커 약 4.7시간 |
K3 토큰 하나가 디스크에서 약 17GB의 전문가를 읽는다. 내장 SSD는 12.78GB/s를 내지만, 테스트한 USB 외장은 0.94GB/s에 그쳤다 — 13배 이상 차이. 반드시 변환된 컨테이너를 내장 NVMe에 둬야 한다. 또한 --budget은 이유가 없으면 건드리지 말 것 — 기본값이 페이징 절벽을 피하도록 설계돼 있고, 직접 크게 잡으면 오히려 8배 느려질 수 있다.
git clone → make → make check. 합성 컨테이너로 엔진 전체가 도는 걸 확인한다. 이어서 ./waste version, ./waste --help로 9개 명령을 훑어보고, tools/diskbench.c를 빌드해 내 SSD의 실제 대역폭을 재 본다(K3를 돌릴 수 있을지 사전 판단).
docs/ENGINE.md와 docs/EFFICIENCY.md를 읽고, "왜 캐시를 키웠는데 8배 느려지는가"를 그림 한 장으로 재구성한다. 그다음 docs/LEARNED.md에서 "폐기된 아이디어" 하나를 골라, 무엇을 가정했고 어떤 측정이 그 가정을 깼는지 요약한다. 코드 없이도 시스템 사고를 크게 키우는 과제.
Kimi-Linear 48B 컨테이너를 준비해 ./waste plan MODEL --budget 4G로 floor·권장·캐시 배분을 확인하고, ./waste bench로 여러 --budget 값에서 tok/s를 측정한다. 내 머신에서도 "정점 이후 하락" 곡선이 재현되는지 직접 그려 본다. README의 K3 표와 모양이 같은지 비교.
ecache.c에서 정책 스위치(policy: 0=LFRU, 1=LRU)를 이용하거나 직접 LRU 경로를 손봐, 같은 트레이스에서 LFRU vs LRU 히트율을 routing_stats.py simulate로 대조한다. "작은 캐시에서 LRU가 5%로 붕괴한다"는 README의 주장을 스스로 재현·반증해 본다.
64GB+ 머신과 1TB NVMe가 있다면 fetch_weights.sh → convert.py로 K3를 변환하고, examples/api_text.c를 참고해 내 C 프로그램에 libwaste를 링크해 토큰 콜백(캐시 히트/미스·I/O 시간 포함)을 받아 진행률 UI를 그린다. 그다음 python3 -m serve로 OpenAI 호환 엔드포인트를 띄워 curl로 대화해 본다.
| 주차 | 주제 | 목표 · 산출물 |
|---|---|---|
| 1주 | C·시스템콜 기초 | 포인터·메모리·read/pread·파일 디스크립터. 파일을 청크로 읽어 합치는 작은 C 프로그램 작성 |
| 2주 | 메모리 계층 & 캐시 | RAM vs 디스크 대역폭/지연, 페이지 캐시, LRU/LFU. diskbench.c로 내 SSD 곡선 측정 |
| 3주 | Transformer & 어텐션 | self-attention·KV 캐시가 왜 문맥 길이에 비례해 커지는지. 작은 GPT를 넘파이로 순전파 |
| 4주 | MoE & 라우팅 | 전문가 혼합·top-k 라우팅·활성 파라미터 개념. K3의 896×top-16이 왜 스트리밍 가능한지 설명 |
| 5주 | 양자화 & VQ | int8/int4, RTN vs 벡터 양자화, 잔차 다단계 VQ. quant_lab.py로 3비트 오차 재현 |
| 6주 | 선형 어텐션(KDA/MLA) | O(1) 상태 어텐션·압축 잠재 KV. docs/KDA.md + src/kda.c 정독 |
| 7주 | waste 캐시·예산 정독 | ecache.c + memory.c + docs/ENGINE.md. 페이징 절벽을 직접 재현·시각화 |
| 8주 | 임베딩 & 서빙 | waste.h로 라이브러리 임베딩 → OpenAI 호환 서버 띄우기. 나만의 진행률 UI 완성 |
배경이 이미 있다면 2주(캐시) → 7주(waste 캐시/예산)만 골라 읽어도 이 레포의 핵심(전문가 캐시 + 페이징 절벽 + 예산기)을 잡을 수 있다. LLM 내부가 궁금하면 4·5·6주가 알짜다.
experts-L{층}.bin). 디스크에 두고 스트리밍하는 대상. "한 전문가 = 한 번의 pread"로 배치.waste.h)이고 CLI·서버는 그 클라이언트라는 설계 철학. 전역 상태 없음, 에러 반환, exit() 금지.