Lumen 사이드 9편 – 측정-주도 의사결정, 직관 11번 중 8번이 틀린 이유

Detailed close up image of a vintage analog weighi

시리즈 전체에서 자주 등장한 표현이 있어요. “가설 11번 중 8번이 부정”, “직관이 측정으로 부정”, “default-off 인프라 유지”. 이번 편이 그 패턴을 본격적으로 풀어볼 시간이에요. 측정-주도 의사결정이라는 마음가짐이 6주 사이드의 가장 큰 학습 자산이었거든요.

한 줄 요약: 건물 짓기 전에 지반 측량부터 한다. 청사진(직관)을 그리기 전에 그 자리 지반(실제 하드웨어 동작)을 측량하지 않으면, 청사진대로 시공해도 무너져요. 책 100번 읽은 직관이 측정으로 부정되는 게 정상이에요.

이번 편이 시리즈에서 사이드 프로젝트 시작하시려는 분에게 가장 도움 될 거 같아요. 기술 디테일보다는 호흡과 의사결정 패턴 얘기예요. 분야가 다르더라도 그대로 적용 가능한 메타 패턴.

정밀 측정 도구 모음

측정-주도 의사결정이 뭔가

측정-주도 의사결정(Measurement-Driven Decision Making, MDDM)은 한 줄로 풀면 이거예요.

가설을 세우면 그걸 짜기 전에 50줄짜리 마이크로벤치부터 짜서 측정으로 확인한다. 측정으로 win이 확정되면 본격 작업, 부정되면 인프라만 살리고 다음 가설로.

도시 비유로 풀면 — 건물 짓기 전에 지반 측량부터 한다. 청사진 그리고 시공팀 부르기 전에 지반이 그 청사진을 받아낼 수 있는지 측량기로 확인. 무너질 지반에 청사진 그리는 건 시간 낭비.

이게 평범한 말 같은데, 실제로 코딩 분야에서 잘 적용 안 됩니다. 보통은 이렇게 가요.

일반 흐름:
  가설 → 코드 작성 (5일) → 측정 → 부정 → 디버깅 (3일) → 결과 부정 확정
  총 8일 소비, 결과물: 부정 1번
측정-주도 흐름:
  가설 → 50줄 벤치 (1시간) → 측정 → 부정/win 결정
    부정: 인프라만 살리고 다음 가설
    win: 본격 작업 5일
  부정 케이스: 1시간 소비
  win 케이스: 1시간 + 5일 = 5일 소비

차이는 부정 케이스에서 8일 vs 1시간. 11번 가설 중 8번이 부정인 분야에서는 이 차이가 사이드의 운명을 가릅니다. 8일 × 8번 = 64일이 1시간 × 8번 = 8시간으로 단축돼요. 사이드 두 달이 사이드 하루가 되는 거예요.

왜 측정해야 하나 – 직관이 자주 틀리는 분야

이걸 왜 직관만으로 가면 안 되냐? 마이크로아키텍처 분야에서는 직관이 자주 틀리거든요. 이 시리즈에서 본 예시들:

  • AVX-512 ZMM (16-lane)이 YMM (8-lane)의 2배 빨라야 한다 (책 상식) → AMD Zen 4에서 -4.5% 회귀 (8편)
  • VNNI는 양자화 매트멀에 항상 빠르다 (책 상식) → 긴 K에서 -27% 회귀 (6편)
  • Prefill batching은 decode 32회보다 빠르다 (책 상식) → 2.9배 회귀 (이번 편에서 상세히)
  • Prefetch는 cache miss를 줄여 빠르게 한다 (책 상식) → 8 thread에서 -49% 회귀 (8편)

이 4가지 다 책에는 “당연히 빨라진다”라고 적혀 있어요. 직관도 그렇게 갑니다. 그런데 측정해 보면 부정. 이유는 책이 말하는 “일반 원리”가 우리 구체적 호스트(AMD Zen 4 + DDR5 + Windows)의 특수 조건을 만나면 변형되기 때문이에요.

건축으로 풀면 — 책에서 “고층 빌딩은 강풍 견디게 짓는다”라는 일반 원리가 있어요. 그 원리는 평지에는 맞아요. 그런데 도시 협곡 효과(빌딩 사이로 강풍 가속)가 있는 지역에서는 그 일반 원리만으로는 부족. 일반 원리 + 그 지역 특수 조건 = 실제 설계. 측정이 “그 지역 특수 조건”을 알려주는 도구예요.

이 사실 받아들이는 게 사이드의 마음가짐 전환이에요. 처음엔 “내가 책 잘 안 읽어서 직관이 틀리는 건가” 자기 의심. 11번 부정 8번 겪고 나면 “아 책은 일반 원리만 알려주고 디테일은 측정해야 한다”가 체득돼요. 다음 사이드 시작할 때부터 자연스럽게 측정 인프라부터 짜게 됩니다.

한 사이클의 모양 – 가설 → 측정 → 결정

한 사이클의 정확한 모양은 이래요.

1. 가설 세우기
   "AVX-512 ZMM이 YMM보다 2배 빠를 것이다"

2. 검증 벤치 짜기 (50~100줄, 1~2시간)
   - 같은 매트멀을 YMM과 ZMM 둘 다로 짜서
   - 같은 입력으로 측정
   - 정답 비트 단위 동일 확인 (correctness)

3. 측정 실행
   - 5번 측정해서 평균 (분산 보기)
   - thread별로 측정

4. 결정 분기:
   - win 확정 (예상치의 80%+): 본격 작업
   - 부정 (예상치의 50% 미만 또는 회귀): 인프라만 살리고 다음 가설
   - 모호 (50~80%): 더 측정하거나 다른 호스트에서 확인

5. 결과 commit message에 기록
   - 측정 데이터를 본문에 포함
   - "측정 안 한 추측"은 적지 않기 (이게 매우 중요)

이 사이클이 짧을수록 사이드의 호흡이 가벼워져요. Lumen은 한 사이클 평균 1~3일. 짧은 가설은 하루, 큰 가설은 일주일. 일주일 가설은 신중히 결정해야 해요. 8.D(prefill batching)가 일주일 가설이었는데 부정으로 끝났어요. 그 사이클 결정은 측정-주도 마음가짐의 대표 사례.

11번 사이클 중 8번 부정 – 정직한 비율

Lumen v0.1.0 → v0.5.0 6주 동안 측정-주도 사이클이 11번이었어요. 정확한 분포:

명확한 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번 들고 와서 절반은 안 맞는 게 정상.

이 비율이 “정상”이라는 사실을 받아들이는 게 첫 번째 마음가짐 전환이에요. 처음엔 부정 한 번 받으면 자기 의심 들거든요. 두 번째도 의심, 세 번째쯤에 “이 분야는 원래 이러구나” 받아들이게 돼요. 부정 8번 겪고 나면 자연스러워져요.

그래도 사이드는 결말을 봤어요. 6주에 v0.5.0 release tag. 핵심은 부정 케이스에서 시간을 최소화한 것. 부정 11번 모두 짧은 사이클로 끝났으니까 누적 부담이 적었어요. 만약 부정 사이클이 평균 일주일이었다면 8주 손실, 사이드 못 끝났을 거예요.

대표 사례 1 – AVX-512 ZMM Zen 4 회귀

5편에서 잠깐 풀었던 일화 자세히. Phase 7.R/S/T 사이클.

가설

“AVX-512 ZMM(16-lane)이 AVX2 YMM(8-lane)의 2배 빨라야 한다.” — 책 상식. 단순 SIMD lane 폭 비례.

작업

AVX-512 인코더 5일 작성. EVEX prefix 4바이트, ZMM 레지스터 인코딩, opmask 등 매뉴얼 100쪽 읽어가며 한 명령씩.

측정

AMD Ryzen 9 7950X (Zen 4):
  YMM baseline: 1.0x
  ZMM:         0.955x (-4.5% 회귀)

“이게 뭐야” 충격. 코드 의심부터 한참 하다가 AMD 문서 깊은 곳에서 발견: double-pumped 구현. Zen 4의 ZMM 명령은 내부적으로 256비트 두 번으로 쪼개져요. 사실상 YMM × 2와 같은데 명령 디코드 오버헤드만 추가.

결정

인프라 폐기 안 함. default-off로 유지. Intel Sapphire Rapids 같은 native 512비트 silicon에서 측정 시 활성화 가능. 한 줄 변경으로 활성화 가능한 상태로 코드 유지.

학습

“책의 일반 원리(ZMM = YMM × 2)가 그 지역 특수 조건(Zen 4 double-pumped)을 만나면 변형된다”는 메타 패턴 체득. 다음 사이드에서 새 명령어 시도할 때 “당연히 빠를 것”이 아니라 “측정으로 확인”이 자동으로 나옴.

흑백 캘리퍼 측정 도구

대표 사례 2 – prefill batching 2.9배 회귀

이게 사이드 후반 가장 충격적인 부정이었어요. Phase 8.D 사이클.

가설

“Prefill batching은 입력 단어 N개를 한 번에 처리하면 같은 weight를 N번 재사용 가능. N=32이면 메모리 효율 32배. decode 32회보다 훨씬 빠를 것.”

이건 진짜 책 상식이에요. weight를 N번 재사용한다는 게 메모리 병목 분야의 정석. 8편에서 다룬 “한 번 도착한 자재로 8가지 시공” 패턴.

작업

일주일 작업. Prefill 인프라 짜고 N=32 매트멀 codegen 추가. 정답성 통과(naive와 비트 단위 동일), 단위 테스트 다 통과.

측정

pp32 (prefill 32 token):  22.65 tok/s
tg32 (decode 32 token):    65.0 tok/s

token당 시간 비교:
  prefill:  44.2 ms
  decode:  15.4 ms
  비율: prefill이 decode보다 2.9배 느림

충격. 가설은 “prefill이 decode보다 5~10배 빠를 것”이었는데 측정은 2.9배 느림. 거의 30배 어긋남.

잘못된 추측 + commit message 부메랑

Phase 8.D.3에서 commit message에 “attention이 원인일 것”이라고 추측 적었어요. 측정 안 한 추측. 그 다음 날 8.D.4에서 attention만 단독으로 측정해 보니 전체 시간의 0.66%. attention이 원인 아니었어요. 자기 자신이 적은 commit message가 다음 날 부메랑처럼 돌아왔어요.

진짜 원인 진단

Phase 8.D.5. 매트멀 raw kernel만 단독 측정. N=32 매트멀 커널이 N=1 × 32보다 2.5배 느림. N>1 codegen이 비효율적이었어요. 작은 동 32개 짓는 것보다 큰 동 한 번에 짓는 도면이 더 비효율이라는 진단.

결정

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

prefill batching 인프라 자체는 유지. native N>1 codegen 최적화는 ROADMAP의 다음 마일스톤으로. 인프라 폐기 안 함.

학습

1) 책 상식(weight 재사용 = 빠름)이 codegen 비효율을 만나면 부정됨. 2) commit message에 측정 안 한 추측 적지 말기. 3) 일주일 작업도 부정될 수 있음. 인프라는 살리되 본격 활용은 다음 cycle. 이 사이클이 사이드 후반의 가장 큰 학습이었어요.

대표 사례 3 – VNNI 부정 후 shape-aware로 부활

6편에서 자세히 풀었지만 측정-주도 관점에서 한 번 더.

1차 사이클 – 단순 적용 (Phase 7.N/7.O)

가설: “VNNI는 양자화 매트멀에 항상 빠르다.” 작업: 1주일. 측정: -2.7% 회귀.

“인프라 폐기?” 일반적인 결정. Lumen은 코드 살림. “다른 호스트나 다른 조건에서 살아날 수도?” 의심.

2차 사이클 – shape-aware (Phase 7.P)

형상별로 분리 측정: 짧은 K는 VNNI win(+10~20%), 긴 K는 loss(-27%). 명확한 패턴.

shape-aware dispatch 도입. 짧은 K는 VNNI, 긴 K는 fp32 4-acc. 1차 사이클의 인프라가 살아남.

학습

부정 후에도 인프라 살린 결정이 미래 win의 근거가 됨. 폐기하면 7.P가 불가능. 이 패턴이 default-off 인프라 유지의 진짜 가치예요.

측정 인프라 – 50줄 벤치가 1주일을 1시간으로

측정-주도가 가능한 핵심은 측정 인프라예요. Lumen의 측정 인프라:

1. naive 구현 (정답 oracle)

매 매트멀에 naive Rust 구현이 같이 있어요. 단위 테스트가 매번 “SIMD 결과 == naive 결과 (비트 단위)”를 확인. 가속 시도하다가 결과 틀어지면 즉시 잡힘.

2. 매트멀 micro-bench (50줄 단위)

매트멀 한 형상을 단독으로 측정하는 50줄 벤치 모음. 한 가설 검증 시간 = 1시간. 일주일 작업으로 가는 게 정당한지 미리 확인.

3. step별 시간 측정

한 단어 만들 때 어디서 시간 가는지 단계별 측정. qkv 5.5ms, wo 2.0ms, gate+up 6.5ms, down 7.0ms, attention 0.7ms 같은 분포. 어디 최적화해야 ROI 큰지 명확.

4. thread별 측정

1/2/4/8 thread 측정. 멀티스레딩이 어디까지 효과적인지 확인. 8.A에서 1t/2t는 win, 8t는 노이즈라는 사실이 이 측정으로 나옴.

5. host별 측정

같은 코드를 다른 CPU에서도 측정. 이건 Lumen에서 부분 적용. Zen 4가 메인이고 Intel은 가끔.

이 인프라 짜는 게 사이드 1~2주차에 들어간 큰 작업이에요. 결과물로는 안 보이지만 그 뒤 5주 사이드 호흡을 결정하는 기초공사. 인프라 없이 짜기 시작했으면 부정 케이스마다 일주일씩 까먹었을 거예요.

commit message에 추측 적지 말기

이건 작은 규칙인데 큰 가치가 있는 규칙이에요. Phase 8.D.3 일화에서 체득.

나쁜 commit message (8.D.3)

Phase 8.D.3: prefill regression diagnosis attempt

prefill가 decode보다 2.9배 느림. 측정 결과 attention 단계가 원인일 가능성.
다음 commit에서 attention 단독 최적화 시도 예정.

“attention 단계가 원인일 가능성”이 측정 안 한 추측. 다음 날 측정해 보니 attention은 0.66%였어요. 자기가 자기 메모를 부메랑으로 받음.

좋은 commit message (8.D.5)

Phase 8.D.5: matmul N=32 kernel measurement

N=32 매트멀 커널을 단독 측정. N=1 × 32 호출보다 2.5배 느림.
attention은 전체의 0.66%로 원인 아님 (8.D.4에서 측정 확인).
N>1 codegen 비효율이 진짜 원인. 다음 commit에서 N=1 fanout 회복 시도.

모든 주장에 측정 데이터가 같이 있어요. “원인일 가능성”이 아니라 “측정으로 확정”의 형태.

이 규칙이 사이드에 미치는 영향: 1) 자기 자신이 미래에 헷갈리지 않음. 2) 회고 블로그 쓸 때 그대로 가져다 쓸 수 있음. 3) 다른 개발자에게 보여줄 때 진정성. Lumen의 17편 회고 블로그가 이 규칙으로 가능했어요.

사이드에 측정-주도가 진짜 필요한 이유

회사 일과 사이드의 가장 큰 차이가 시간이에요. 회사 일은 매일 8시간 쓸 수 있고 동료들과 협업하니까 한 잘못된 결정의 비용이 분산돼요. 사이드는 매일 1~2시간, 본인 혼자. 한 잘못된 결정의 비용이 그대로 본인에게 전부 와요.

그래서 사이드에 측정-주도가 더 중요해요. 한 사이클을 짧게 끝낼 수 있어야 매일 1~2시간으로 6주에 결말을 봐요. 안 그러면 한 부정 사이클에 일주일 날리고, 그게 2~3번 누적되면 사이드 중단.

저도 작년 사이드들이 흐지부지 끝난 패턴 중 하나가 이거였어요. 측정 없이 짜다가 막혀서 며칠 헤매다가 동기 부여 떨어져서 손 놓음. Lumen은 의식적으로 측정 인프라부터 짰어요. 그게 6주에 결말 본 핵심 요인 중 하나.

사이드 시작하시려는 분이 이 글에서 가장 가져가야 할 메시지가 이거예요. “첫 commit 다음에 측정 코드를 넣어라”. 그게 사이드의 호흡을 정합니다. 직관 11번 중 8번이 부정인 분야에서도 사이드가 결말을 보게 만드는 마음가짐이에요.

9편 정리

  1. 측정-주도 의사결정은 가설 → 50줄 벤치 → 측정 → 결정 사이클. 부정 케이스를 1시간에 끝내고 win 케이스만 본격 작업.
  2. 마이크로아키텍처 분야는 직관이 자주 틀린다. AVX-512 ZMM Zen 4, VNNI 긴 K, prefetch 8 thread, prefill batching 모두 책 상식이 측정으로 부정.
  3. 11번 사이클 중 8번 부정이 정상. 비율 받아들이고 부정 케이스 시간 최소화가 사이드 결말의 핵심.
  4. 인프라 살리기의 가치: 7.O 부정 → 7.P 부활 패턴. default-off로 유지하면 다른 조건에서 미래 win.
  5. commit message에 추측 적지 말기: 측정 안 한 추측은 다음 날 부메랑. 모든 주장에 측정 데이터.
  6. 측정 인프라 5요소: naive oracle, micro-bench, step별, thread별, host별. 사이드 1~2주차에 짜야 그 뒤 호흡이 가벼움.
  7. “첫 commit 다음에 측정 코드를”: 사이드 시작하는 분에게 가장 중요한 한 줄.

이번 편이 시리즈에서 가장 메타 패턴이 진한 영역이에요. 기술 분야가 LLM 추론 컴파일러가 아니어도 똑같이 적용됩니다. 임베디드, 분산 시스템, 모바일 앱, 모든 분야의 사이드에서 측정-주도는 핵심이에요.

다음 편 미리보기

10편은 마지막. 6주 회고 — v0.1에서 v0.5까지. 시리즈 전반의 추상적 패턴이 실제 6주 사이드 안에서 어떻게 흘렀는지 시간 순으로. v0.1.0(첫 토큰 “안녕”)에서 v0.5.0(release-tagged)까지의 시공 일지. 무엇을 배웠는지, 어떻게 끝냈는지.

그 다음은 0편(시리즈 인덱스). 처음 시리즈 펴는 분의 입구 역할. 비유 매핑 표와 추천 순서를 정리.

이 시리즈 본문 전체는 GitHub에도 정리해 뒀어요. github.com/redchupa/lumen/docs/tutorial에서 원문 격으로 보실 수 있어요.

그럼 다음 편에서 뵐게요. 측정-주도 패턴 또는 본인 사이드 경험 관련 댓글 남겨주세요.

댓글 달기

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

위로 스크롤