Lumen 사이드 10편 – 6주 회고, v0.1에서 v0.5까지의 시공 일지

A detailed planner with goals and colorful tabs on

이번 편이 시리즈 마지막 본문이에요. 1편부터 9편까지 추상적으로 풀어둔 측정-주도 패턴이 실제 6주 사이드 안에서 어떻게 흘렀는지를 시간 순으로 정리할게요. v0.1.0에서 v0.5.0까지 어떤 결정을 했고, 어떤 측정을 했고, 무엇이 부정됐는지의 일기.

추상적 개념이 구체적 일기를 만나면 “내 사이드 프로젝트도 같은 패턴으로 가능”이 보일 거예요. 사이드 시작하시려는 분이라면 이 편이 가장 손에 잡히게 느껴질 거 같아요. “6주에 이 정도 가능”이라는 그림이 잡히면 시작 결심이 쉬워지거든요.

저도 사이드 시작 전엔 “6주에 LLM 추론 컴파일러 가능?” 의심이 컸어요. 막상 끝나고 보니 가능했고, 그 호흡이 어떻게 흘렀는지 정리해 둘 가치가 있어요. 사이드 끝낼 줄 모르는 분이 가장 많이 막히는 패턴이라.

5월 플래너 노트북

출발점 – 빈 부지에 측량 말뚝 박기

6주 전 첫 commit은 단순했어요. Rust 프로젝트 골격, 빈 Cargo.toml, 한 줄짜리 README. “LLM 추론 컴파일러 만들겠다”는 의도만 적힘. 건축으로 말하면 부지를 구하고 측량 말뚝 몇 개 박은 상태. 아직 청사진도 자재도 없음.

이 시점의 결정 사항:

  • 목표: 한국어 LLM(Qwen2.5-0.5B)을 처음부터 짠 컴파일러로 추론
    • 건축으로: “한국식 도시 한 채를 자기 손으로 짓겠다”
  • 언어: Rust (메모리 안전성, 성능, 개인 학습 욕구)
    • 건축으로: “주 자재는 Rust 강철. 안전 인증 자동 통과”
  • 백엔드: x86_64 우선, ARM64는 나중에
    • 건축으로: “본사 지역 기후(x86) 기준 먼저, 다른 기후는 v0.5 이후”
  • 양자화: Q8_0부터 (가장 흔한 포맷)
    • 건축으로: “cm 단위 청사진부터, mm 단위는 다음 cycle”
  • 비교 대상: llama.cpp (ggml). 같은 모델·같은 측정으로 비교
    • 건축으로: “옆 동네 거대 건설사가 지은 빌딩을 매번 옆에 두고 비교”
  • 호흡: 1주에 commit 5~10개. 매일 1~2시간
    • 건축으로: “1주에 도면 5~10장씩. 무리 안 하고 꾸준히”

이 결정들이 6주 끝까지 거의 안 바뀌었어요. 첫 측량 말뚝이 단단해야 사이드 프로젝트가 지속됩니다. 결정 자체에 일주일 쓸 게 아니라 의도만 한 줄 적고 시작하는 게 흐지부지 안 되는 패턴이에요.

v0.1.0 (1~2주차) – 첫 동 올리기

첫 2주는 인프라 짜는 시간이었어요. 결과물은 없고 코드 양만 늘어요. 건축으로 말하면 청사진 그리고 기초공사 하는 시기. 외부에서 보면 “아무것도 안 짓고 있는 것 같은데?” 싶은 시기예요. 이 시기 흐지부지되기 쉬운데, 작은 milestone을 정해두는 게 도움이 됐어요.

Phase 1 – DSL · 파서 (스케치 → 평면도 변환기)

examples/matmul.lum 파일이 파싱되고 타입 체크 통과. 21개 단위 테스트.

fn matmul(a: tensor<f32, [4, 8]>, b: tensor<f32, [8, 2]>) -> tensor<f32, [4, 2]> {
    return a @ b;
}

이 한 줄이 처음 컴파일러를 통과한 순간 진짜 작은 감동. 건축으로 말하면 손 스케치 한 장이 평면도 변환기를 통과해서 정식 도면 형식으로 출력된 순간. 코드는 단순한데 그 뒤에 있는 lexer/parser/type-checker가 다 작동하는 시그널.

Phase 2.A – IR + C backend (시공 도면 + 기존 시공팀 검증)

IR을 만들고 그걸 C 코드로 출력. naive matmul이 정답인지 검증 (reference oracle). C로 컴파일해서 결과 비교. 건축으로 말하면 자기 시공 도면을 일단 옆 동네 기존 시공팀에게 넘겨서 “이대로 지으면 정답이 나오는지” 확인받은 단계.

Phase 2.B – 자체 x86_64 backend (자체 시공팀 첫 시공)

직접 머신 코드 emit. 매트멀 결과가 naive Rust와 비트 단위 동일. 첫 JIT 실행이 여기서 일어남. 자체 시공팀이 처음으로 자기 손으로 벽돌 하나 쌓은 순간.

Phase 3 – AVX2 자동 합성 (1차선 → 8차선 도로)

SIMD 명령 인코더, AVX2 매트멀 path. 스칼라 대비 8.7배 가속(작은 매트멀 micro-bench).

Phase 3.C – Register tile 4×8 (정산 창고 4개 + 4×8 구역)

4-acc 패턴 + 4×8 tile. 스칼라 대비 19배 가속. 57 GFLOPS 도달.

Phase 5.C – Fused quant matmul (cm 청사진 직접 시공)

Q8 weight + F32 activation 매트멀 IR로 자동 합성. 이 시점에서 컴파일러 인프라가 거의 갖춰짐. mm 청사진을 cm로 단순화한 자재를 머릿속 변환으로 그대로 시공.

Phase 6 – Qwen2.5-0.5B 한국어 추론 (첫 동 입주)

“안녕” 입력 → “안녕하세요, 저는” 출력. 첫 진짜 LLM 답. 1주차에 짜기 시작한 코드가 6주차에 ChatGPT 비슷한 거 동작. 건축으로 말하면 첫 동이 올라가고 첫 입주민이 들어온 순간.

여기까지 v0.1.0. 단일 스레드 4.43 tok/s. ggml 41 tok/s의 9.3배 느림.

처음 봤을 땐 9.3배 격차가 막막했어요. 옆 동네 거대 건설사 빌딩은 같은 시간에 9배 빨리 짓더라구요. 그런데 v0.1.0 = “동작은 한다”가 목표였고 그건 달성했죠. 속도는 다음 cycle 일.

1~2주차 배움

  • 첫 commit은 골격만. 처음부터 완벽하게 짜려 하지 말기. 측량 말뚝부터.
  • 작은 milestone 자주 정하기. “이번 주 안에 matmul 한 줄 파싱 통과” 같은 거.
  • naive 구현부터. 처음부터 빠르게 짜려 하면 디버깅 지옥. 정답 만들고 나중에 가속.

v0.2.0~v0.3.0 (3~4주차) – 자재 운송과 공사팀 늘리기

이 시기가 가장 큰 perf 가속이 일어난 시기예요. 건축으로 말하면 자재 운송 방식을 개선하고 공사팀을 늘리는 시기. 매주 win이 보이는 시기라 동기 부여 가장 높을 때.

Phase 7.D – Q8 native fused matmul (자재 변환소 우회)

Q8 weight를 dequantize하지 않고 그 자리에서 매트멀. Phase 5.C의 IR 자동 합성을 model path까지 연결. 4.43 → 17.97 tok/s (+4배 가속).

이건 직관이 맞은 케이스. 건축으로 말하면 “현장에서 cm 자재를 mm로 변환하는 중간 창고를 없애고 머릿속에서 즉석 변환하면 운송 부담 4배 줄어 빠를 것”. 측량으로 확정. 이때 가설-측량 사이클이 제대로 작동한 첫 사례였어요.

Phase 7.G – Q8 N=1 4-acc 커널 (정산 창고 4개로 분산)

5편에서 본 4-accumulator 패턴. block당 4개 독립 acc. 17.97 → 29.10 tok/s (약 2배 가속). 건축으로 말하면 정산 창고가 하나여서 자재 트럭이 줄 서던 걸 창고 4개로 분산.

또 직관이 맞은 케이스. FMA dependency chain 끊으면 빠를 것 → 측량으로 확정.

Phase 7.J – Multi-thread (rayon, 공사팀 8명으로 늘리기)

멀티스레딩 첫 시도. rayon 라이브러리 활용. 29 → 60 tok/s @ 8 thread. ggml과의 8t 격차가 약 1.5배까지 좁혀짐. 건축으로 말하면 한 동을 8명이 분담 시공. 다만 자재 우물은 여전히 하나라 8배가 안 나옴.

이 시기 약 2주 동안 v0.1 대비 13배 가속. 매주 win이 보이는 시기. 동기 부여 가장 높을 때.

3~4주차 배움

  • 큰 가속은 보통 명확한 직관에서 옴. Q8 native, 4-acc, 멀티스레딩. 다 건축 상식.
  • 직관 win 사이클은 짧음. 이 시기는 빨리 다음 단계로.
  • naive 구현 만들어 둔 게 도움 됨. 매 가속 시도 후 정답이 나오는지 비트 단위 비교. 자재 운송 방식을 바꿔도 결과 빌딩은 같아야 함.

v0.3.0~v0.4.0 (5주차) – 신소재 실험과 첫 부정

이 시기에 첫 진짜 부정 사례들이 나옵니다. 건축으로 말하면 신소재를 들고 와서 시공해 봤는데 기대만큼 안 나오는 시기.

Phase 7.M – Q8 int dot (정수 자재 단가 인하 시도)

정수 dot product 가속 시도. 측정 결과 거의 변화 없음 (net-neutral). 자재 단가는 싸졌는데 시공 시간엔 거의 영향 없음. 인프라(자재 매뉴얼)는 유지.

Phase 7.N – VNNI single-acc (신형 시공 장비 단일 운용)

VNNI 명령어(vpdpbusd) 활용. 단일 acc로 시도. -3.6% 회귀. 폐기. 신형 장비를 정산 창고 1개로 운용하니까 오히려 줄 서서 느려짐.

Phase 7.O – VNNI 4-acc (신형 시공 장비 + 정산 창고 4개)

VNNI에 4-acc 결합. 그래도 -2.7% 회귀. 다만 인프라(시공 매뉴얼)는 유지. 폐기 안 함.

Phase 7.P – Shape-aware dispatch (구역별 적정 장비 배치)

매트멀 형상별로 다른 커널 선택. 짧은 K는 VNNI, 긴 K는 fp32 4-acc. 여기서 7.O의 인프라가 살아남. 짧은 K 구역에서 VNNI win이 활성화되니까. 60 → 65 tok/s @ 8t.

이 사이클이 정확히 “default-off 시공 매뉴얼이 부활하는 패턴”이에요. 7.O 부정 후 그 매뉴얼 폐기했으면 7.P가 불가능했을 거. 매뉴얼 살린 결정이 미래 win의 근거가 됨.

v0.4.0 시점 standing:

  • 단일 스레드: 41.85 tok/s (ggml 40.40보다 +4% 빠름)
  • 8 thread: 65.5 tok/s (ggml 90.9의 약 1.39배 느림)

처음으로 단일 스레드에서 ggml과 비등해진 시점이에요. 1명 시공팀 효율은 대등했고 8명 시공팀은 여전히 격차.

5주차 배움

  • 부정 사례가 처음 누적되기 시작. 자기 의심 시작되는 시기.
  • 시공 매뉴얼 살리기의 진짜 가치를 처음 느낌. 7.O → 7.P 부활 패턴.
  • “1주에 win 없어도 OK”라는 마음 가짐 필요. 매뉴얼 누적이 진척.
나무 표면의 빈 노트북

v0.4.0~v0.5.0 (6주차) – 지반 측량과 큰 가설 부정

이 시기가 측정-주도 패턴의 핵심 사이클이었어요. 건축으로 말하면 본격적으로 지반 측량 장비를 동원해서 격차 원인을 진단하고, 그 진단을 기반으로 큰 시공 계획을 세웠다가 그 계획 자체가 부정되는 사이클.

Phase 7.U – 격차 분석 (전체 지반 측량)

1.39배 8t 격차의 원인 진단. step-별 시간 측량, thread별 측량, memory bandwidth 측량. 결론: 자재 우물 활용도 차이(Lumen 55%, ggml 74%)가 격차의 정체. 여기서 의외의 발견: 단일 스레드 시공팀 효율은 Lumen이 ggml보다 +13% 빠름.

이게 v0.5 cycle의 가장 큰 발견이었어요. 격차 원인이 명확해진 시점. 건축으로 말하면 “8명 팀 격차의 원인은 시공팀 실력이 아니라 자재 우물 처리량이다”라는 진단.

Phase 8.A – Atomic ThreadPool (번호표 발급기 도입)

mutex 기반 → atomic counter 기반 재설계. 1 thread +9%, 8 thread +3%. 8 thread는 noise 영역이지만 1~2 thread에서 명확한 가속. merge. 시공팀 작업 할당 방식을 큰 회의 대신 번호표 발급으로 바꾼 효과.

Phase 8.B – chunk L2-fit cap (자재 보관 L2 창고 맞춤)

cache fit 시도. -3.5% 회귀. revert. 자재 한 묶음을 L2 창고 크기에 맞춰 나눠봤는데 오히려 운송 횟수가 늘어 느려짐.

Phase 8.C – Software prefetch (다음 자재 미리 보내기 신호)

prefetch 명령 추가. 1 thread 약간 win, 8 thread -49% 회귀. revert. 1명 시공팀에서는 win. 8명에서는 자재 운송 도로가 미리 보내기 신호로 더 혼잡해져서 절반 속도. 인프라(prefetch encoders)는 유지.

Phase 8.D – Prefill batching (대량 시공 일괄 처리 계획)

prefill 인프라 1주일 작업. 정답성 통과, 단위 테스트 다 통과. 측정 결과 pp32 = 22.65 tok/s, tg32 65의 token당 2.9배 회귀. 충격적 결과. 1주일 들여 짠 대량 시공 계획이 오히려 느렸음.

Phase 8.D.3 commit message에 “attention이 원인일 것”이라 추측 적음. 측량 안 한 추측이었음.

Phase 8.D.4 (다음 날). attention만 단독으로 측량. 24 layers × 389µs = 9.3ms. pp32 전체의 0.66%만. 가설 부정. commit message에 추측 적은 거 부메랑. 다음 날 자기가 자기 메모 보고 비웃음.

Phase 8.D.5. 진짜 원인 찾기. 매트멀 raw kernel만 측량. N=32 커널이 N=1 × 32보다 2.5배 느림. N>1 codegen 비효율 확정.

Phase 8.E.1 – N=1 fan-out 회복

임시 fix. N>1 dispatcher가 N=1 커널을 N번 호출. pp32 22.65 → 54.15 tok/s (+2.4배 회복). tg32까지는 못 따라가지만 회귀 탈출.

여기까지 v0.5.0. 이 시기에 부정 6번, win 2번, 진단 1번, 회복 1번. 1주에 9번 사이클.

6주차 배움

  • 1주일 1주제(prefill batching) 시도하면 큰 가설이 부정될 수 있음. 그래도 시공 매뉴얼은 부활 대기로 남김.
  • commit message 추측이 다음 날 부메랑. “측량 안 한 추측 적지 말기” 규칙 강화.
  • 50줄 단위 bench가 진짜 강력. 1주일 디버깅을 1시간으로 단축. 작은 측량 도면이 전체 시공 일정을 좌우.

6주 결과 정리

수치

구분v0.1.0v0.5.0
단일 스레드 tg324.43 tok/s45.64 tok/s (10.3배 가속)
8 스레드 tg32미지원67.4 tok/s
vs ggml 단일9.3배 느림+13% 더 빠름
vs ggml 8t미지원1.30배 느림
측정-주도 사이클0회11회 누적
회고 블로그 편수0편17편

10배 가속(전 cycle 총합)이 인상적이지만 그것보다 단일 스레드 시공팀에서 ggml +13%이 가장 큰 자산.

부정과 win 비율

명확한 perf win:  3번 (Phase 7.D, 7.G, 7.P)
직관 win:         1번 (Phase 8.A - 1t/2t)
진단 확정:        1번 (Phase 8.D.5)
부분 회복:        1번 (Phase 8.E.1)
net-neutral:      1번 (Phase 7.M)
부정 (revert):    4번 (Phase 7.N, 8.B, 8.C, partial 7.O)
부정 (인프라 유지): 5번 (Phase 7.R/S/T, prefetch infra, ZMM infra,
                       prefill plumbing, false retro)

정직히 부정 비율 50% 이상. 이게 사이드 프로젝트 6주 분량의 정직한 비율이에요. 신소재 11번 들고 와서 절반은 안 맞는 게 정상.

자산

  • v0.5.0 GitHub Release (준공 도장 박힌 동 5개)
  • 17편 회고 블로그 (시공 일지)
  • 11회 사이클 측량 데이터 (지반 측량 데이터베이스)
  • 정답성 보장 (모든 commit이 naive와 bit-identical, 모든 동의 도면 검증 완료)
  • ROADMAP에 다음 cycle 명확 (다음 시공 계획서)

무엇을 배웠나 – 5가지 핵심

6주 동안 가장 크게 배운 것 5가지.

1. 직관은 정직히 자주 틀린다

ZMM, prefetch, batching 등 건축 책의 상식이 이 지역 기후(마이크로아키텍처)에서는 자주 부정. 측량 없이 갔으면 11번 중 절반 잘못된 방향이었을 거.

2. 시공 매뉴얼 폐기는 미래 비용

7.O의 7.P 부활 패턴이 명확한 증거. default-off로 유지하는 게 진짜 자산. 미래 호스트/조건(다른 지역 기후)에서 활성화 가능.

3. 단일 측량의 의미는 한정적

시공팀 수별, 형상별, 호스트별로 다 측량해야 진짜 패턴이 보임. 한 차원만 보면 잡음에 속음.

4. 시공 일지로 풀면 메타 패턴 발견

17편 회고 블로그 쓰면서 “commit message에 추측 적지 말기” 같은 규칙이 자기 자신에게 보임. 글 안 썼으면 같은 함정 반복했을 거.

5. 잘 끝낼 줄 알기

v0.5.0 release tag 박고 maintenance mode 선언. 6주 분량 + 정직한 엔지니어링 자산이 누적된 상태에서 멈춤. 준공 도장 박는 시점이 중요. 작년 9개월 흐지부지된 다른 사이드보다 잘 끝남.

이 시리즈를 마치며

10편 + 0편 시리즈로 LLM 추론 컴파일러를 처음부터 짓는 과정을 풀었어요. 결국 이 시리즈에서 다룬 게 무엇인가 정리하면.

기술적 (시공 기술)

  • LLM 추론 = 매트멀 + 자재 운송
  • 컴파일러 = 스케치 → 평면도(AST) → 시공 도면(IR) → 작업 지시서(코드)의 4단계
  • JIT = 시공 시점에 도면을 즉석 변환해서 망치 휘두르기
  • SIMD = 1차선 도로를 8차선으로 넓혀 자재 동시 운송
  • 양자화 = mm 청사진을 cm로 단순화 (결과 빌딩 거의 동일)
  • Transformer = 한 동의 8단계 시공(매트멀 5개 + attention + 부수)
  • 멀티스레딩 = 시공팀 8명 늘려도 자재 우물 하나면 8배 안 나옴

철학적 (시공 철학)

  • 측량 없이 직관만으로 가면 절반 잘못됨
  • 부정도 자산. default-off로 시공 매뉴얼 유지
  • 사이드 프로젝트는 준공 도장 박을 줄 아는 것도 능력

이 시리즈의 끝에서 새 사이드 프로젝트 시작하시는 분이 첫 commit 다음에 “측량 코드”를 넣게 됐다면, 이 시리즈가 의미 있게 도움 된 거예요. 건축 책 100번 읽는 것보다 직접 한 채 지어보는 것의 가치가 그 한 줄 측량 코드에 들어 있습니다.

저도 이 시리즈 쓰면서 다시 한 번 정리됐어요. 6주 진행 중에는 매 phase 회고 17편을 따로 썼는데, 시리즈로 묶어보니 “전체 흐름”이 보였어요. 기술적으로는 컴파일러·SIMD·양자화 같은 분야지만, 메타 패턴으로는 어떤 사이드든 적용 가능한 호흡이라는 것도 다시 확인했고요.

10편 정리

  1. v0.1 → v0.5 = 6주 작업. 단일 스레드 10.3배 가속. 8 스레드 1.30배 격차. 가장 큰 자산은 1t 시공팀에서 ggml +13%.
  2. 시기별 패턴 차이: 1~2주차는 인프라(첫 동 올리기), 3~4주차는 큰 가속 (자재 운송 개선 + 팀 늘리기), 5~6주차는 부정 사례 누적과 지반 진단.
  3. 부정 사례가 절반 이상. 정직한 비율. 매번 시공 매뉴얼은 유지.
  4. commit message가 미래의 자기를 비웃을 수 있다. 측량 안 한 추측 적지 말기.
  5. 잘 끝낼 줄 알기. release tag + maintenance mode. 9개월 흐지부지보다 6주 준공 도장이 더 자산.

시리즈 종료

여기서 본문 10편은 끝입니다. 이 시리즈 전체 인덱스와 짧은 요약은 다음에 올릴 0편(시리즈 인덱스)에서 정리해 드릴게요. 처음 시리즈를 펴는 분은 0편부터 보시는 게 도움이 됩니다.

새 사이드 프로젝트 시작하시면 화이팅. 첫 commit 다음에 측량 코드 넣는 거 잊지 마세요. 도시 한 채 지어보면 다음 도시는 훨씬 빠릅니다.

그동안 시리즈 따라와 주신 분들 감사해요. 사이드 시작하시려는 분 계시면 언제든 댓글 남겨주세요. 같이 정리해 가면 좋겠어요.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤