Lumen 사이드 5편 – SIMD, 1차선 도로를 8차선으로 바꾸는 기술

Aerial shot of a traffic filled highway interchang

4편에서 JIT(Just-In-Time)이 어떻게 메모리에 어셈블리를 쓰고 그걸 함수처럼 호출하는지 풀었어요. 이번 편은 그 어셈블리를 어떻게 빠르게 만드냐가 주제예요. SIMD라는 기술. 매트멀이 빨라지는 가장 큰 단일 요인이에요.

SIMD는 Single Instruction Multiple Data의 약자예요. CPU 한 명령으로 여러 숫자를 동시에 처리하는 기술. 도시 비유로 풀면 “자재 운송 도로를 1차선에서 8차선으로 넓히는 일”이에요. 같은 자재량을 8배 빠르게 옮길 수 있죠.

이번 편이 좀 깁니다. SIMD가 진짜 핵심 주제거든요. AVX2·AVX-512 같은 명령어 세트, FMA(Fused Multiply-Add), 4-accumulator 패턴, 그리고 AMD Zen 4에서 AVX-512가 회귀한 일화까지. 처음엔 다 막연하지만 도로 비유 하나로 다 잡혀요.

모던 하이웨이 차선 표시 항공 뷰

일반 CPU 명령은 1번에 1개 – 1차선 도로

먼저 일반 곱셈을 봅시다. CPU에게 “두 float을 곱해서 결과를 변수에 저장해라”라고 시키는 어셈블리는 이렇게 생겼어요.

mulss xmm0, xmm1     # xmm0 = xmm0 * xmm1 (32비트 float 한 개)

xmm0, xmm1이 CPU의 레지스터라고 부르는 임시 저장 공간입니다. 한 레지스터에 한 숫자가 들어가요. 이 명령으로 한 번에 한 곱셈만 가능.

도시 비유로 풀면 — 1차선 도로예요. 자재 트럭 한 대가 한 번에 한 대씩 지나가요. 한 자재(곱셈) 처리하려면 한 트럭이 입구로 들어와서 시설에서 처리받고 출구로 나가는 시간만큼 걸립니다.

문제는 매트멀 계산할 때 곱셈을 엄청 많이 해야 한다는 거예요. 1편에서 매트멀 한 번에 약 870만 번 곱셈이 필요하다고 했죠. 한 번에 한 곱셈이면 870만 번 명령을 실행해야 합니다. CPU가 한 명령 처리하는 데 약 4 사이클 걸린다 치면 3500만 사이클. 3GHz CPU면 약 0.012초. 1차선 도로에 트럭 870만 대가 줄 서서 한 대씩 지나가는 거예요.

이걸 80% 줄일 수 있다면 어떨까요. SIMD가 바로 그걸 합니다. 사실 더 줄어요 — 8배 가속이니까 12%만 남습니다.

SIMD – 한 명령에 여러 데이터, 8차선 도로

SIMD 명령은 한 명령으로 여러 데이터를 동시에 처리합니다. 예를 들어 AVX2의 vmulps 명령은:

vmulps ymm0, ymm1, ymm2     # ymm0 = ymm1 * ymm2 (32비트 float 8개 동시에)

ymm0, ymm1, ymm2는 256비트 레지스터예요. 32비트 float이 8개 들어갑니다. 이 한 명령으로 곱셈 8번을 동시에 처리해요.

도시 비유로 풀면 — 8차선 도로예요. 자재 트럭 8대가 동시에 도로를 지나가요. 같은 시간에 처리량이 8배.

매트멀에서 870만 번 곱셈이 필요하다고 했죠. SIMD로 한 번에 8개씩 하면 명령 횟수가 8분의 1, 약 110만 번. 같은 4 사이클이면 440만 사이클, 약 0.0015초. 8배 가속이에요.

이게 SIMD의 한 줄 요약입니다. 한 명령으로 8개(또는 16개) 데이터를 동시에 처리.

저도 사이드 처음 시작할 때 SIMD가 진짜 막연했어요. “한 명령으로 여러 개?”가 추상적이었거든요. 막상 어셈블리 직접 짜보니까 진짜 그래요. vmulps 한 줄이 진짜로 8개 float을 동시에 곱하더라구요. 어셈블리 한 명령이 1주일 작업의 1/8을 줄이는 거예요. 이걸 처음 측정으로 확인했을 때 정말 신기했어요.

SIMD 레지스터 크기 – XMM, YMM, ZMM 차이

x86_64 SIMD 레지스터는 세 가지 크기가 있어요. 시간이 흐르면서 점점 더 큰 게 추가됐어요.

레지스터크기float 32비트 몇 개명령어 세트도로 비유
XMM128비트4개SSE (1999년~)4차선
YMM256비트8개AVX/AVX2 (2011년~)8차선
ZMM512비트16개AVX-512 (2016년~)16차선

같은 CPU에서 다 사용 가능하지만 새 명령어 세트일수록 한 번에 더 많이 처리해요. ZMM은 한 명령으로 float 16개. 이론상 XMM의 4배 가속이 가능합니다.

ARM도 비슷한 게 있어요. ARM의 SIMD는 NEON이라고 부르고, 보통 128비트(float 4개)입니다. Apple Silicon 같은 ARM CPU는 NEON을 써요. 최근에는 SVE(Scalable Vector Extension)라는 더 큰 SIMD도 있는데, 길이가 가변적이라 컴파일러가 다루기 까다로워요. Lumen은 x86_64 AVX2 위주로 짰고, ARM64 NEON 지원은 향후 ROADMAP에 있어요.

호환성 문제도 있어요. AVX-512는 2016년부터 등장했지만, 일반 데스크탑 CPU에는 늦게 들어왔어요. Intel은 2023년 13세대 Raptor Lake에서 AVX-512 빼버렸고, AMD는 Zen 4(2022년)부터 지원. 그래서 “이 코드가 어디서 동작할 거냐”가 중요한 결정이에요. AVX2는 거의 모든 최근 10년 CPU에서 동작하지만, AVX-512는 호스트 한정.

SIMD가 빠르지 않은 경우 – 도로가 막힐 때

SIMD가 8배 가속이라고 했지만 항상 그런 건 아닙니다. 실제로는 여러 제약이 있어요. 8차선 도로 만들어도 도로가 막히면 1차선 도로처럼 동작.

1. 데이터가 정렬돼 있어야 함

SIMD 명령은 메모리에서 한 번에 8개를 읽는데, 그 8개가 연속된 메모리에 정렬돼 있어야 빠릅니다. 흩어져 있으면 별도 명령으로 모아야 하고 속도가 떨어져요.

8차선 도로 비유로 풀면 — 트럭 8대가 같은 방향으로 줄지어 입구에 도착해야 동시에 들어갈 수 있어요. 8대가 흩어져 다른 시간에 오면 1차선 도로처럼 동작합니다.

2. 같은 연산을 모든 데이터에 적용해야 함

“8개 곱셈”은 가능하지만 “1개 곱셈 + 7개 더하기”는 한 SIMD 명령으로 안 됩니다. 매트멀처럼 같은 연산이 반복되는 계산에 잘 맞아요.

도로 비유로 풀면 — 모든 8대가 같은 출구로 가야 한 번에 처리 가능. 7대가 A 출구, 1대가 B 출구로 가면 8차선이라도 두 번에 나눠 처리.

3. 분기가 없어야 함

“if 양수면 곱하기, 음수면 더하기” 같은 분기는 SIMD가 약점. 8개 데이터 중 일부만 양수면 처리가 복잡해집니다. 매트멀은 분기가 없으니 SIMD에 적합해요.

4. 메모리 대역폭이 충분해야 함

SIMD가 8배 빠르게 계산해도 자재(데이터)를 메모리에서 가져오는 속도가 8배 빨라지지 않으면 무의미. 8차선 도로 만들었는데 자재 창고 입구가 좁아서 트럭 8대가 동시에 못 나오면 8차선이 의미 없어요. CPU가 데이터를 기다리며 idle 상태가 됩니다. 1편에서 다룬 “자재 운송 병목”이 여기서 다시 등장.

요약하면 SIMD는 연속된 데이터에 같은 연산을 반복하고, 분기가 없고, 메모리에서 빨리 가져올 수 있을 때 가장 빛납니다. 매트멀은 이 4가지를 다 만족해서 SIMD의 가장 모범 사례 중 하나예요.

이게 사이드 처음 시작할 때 잘 와닿지 않았어요. “SIMD가 8배 빠르다고 책에 나오는데 왜 우리 측정은 5배밖에 안 되지?” 싶었거든요. 다 짜고 측정한 다음에야 “메모리 대역폭이 병목이었구나” 깨달았어요. 8차선 만들어도 창고 입구가 좁으면 안 된다는 거. 이게 7편 멀티스레딩에서 또 한 번 등장해요. 메모리 병목은 시리즈 전체를 관통하는 주제거든요.

SIMD로 매트멀 짜는 기본 패턴

매트멀의 한 줄은 이렇게 생겼어요.

out[k] = sum_{i} a[i] * b[i, k]    (i = 0..K-1)

K가 896이라면 곱셈 + 누적 896번. 일반 명령으로 짜면:

let mut acc: f32 = 0.0;
for i in 0..896 {
    acc += a[i] * b[i * N + k];
}
out[k] = acc;

SIMD로 짜면 8번씩 동시에 처리하므로 반복 횟수가 112번(896 / 8). 어셈블리는 이런 모양이에요.

vxorps ymm0, ymm0, ymm0      # ymm0 = [0, 0, 0, 0, 0, 0, 0, 0]
loop:
    vmovups ymm1, [a + i*4]                  # a의 8개 float을 ymm1에 로드
    vfmadd231ps ymm0, ymm1, [b + i*4*N + k*4] # ymm0 += ymm1 * b의 8개 float
    add i, 8
    cmp i, 896
    jl loop
# ymm0의 8개 lane을 다 합쳐서 acc 만들기 (horizontal reduce)
vhaddps ymm0, ymm0, ymm0
...

vfmadd231ps가 매트멀의 핵심 명령이에요. 이게 시리즈에서 자주 등장하는 명령이에요. 다음 절에서 자세히 풀어볼게요.

FMA – 곱셈과 덧셈을 한 명령에

vfmadd231psFMA(Fused Multiply-Add)의 약자로 “곱셈과 덧셈을 한 명령에 처리”하는 명령이에요.

  • 일반 SIMD: vmulps로 곱하고 vaddps로 더하면 2명령
  • FMA: acc = acc + a * b를 한 명령

곱셈과 덧셈을 한 사이클에 처리하니까 또 2배 빨라져요. 도시 비유로 풀면 — 자재 운반 트럭과 자재 결합제 트럭을 두 대 따로 보내는 게 일반 SIMD고, 한 트럭에 자재와 결합제 같이 싣고 한 번에 가는 게 FMA예요.

AVX2 SIMD에 FMA를 더하면 스칼라 대비 약 16배 이론 가속 가능. 실제로는 메모리 병목 때문에 8배 정도가 실용 한계.

FMA의 또 다른 장점: 정밀도가 더 좋다는 거예요. 일반 곱셈+덧셈은 곱셈 결과를 메모리에 저장했다가 다시 읽어 덧셈하는데, 그 사이에 부동소수점 반올림이 일어나요. FMA는 곱셈+덧셈을 내부에서 한 번에 처리해서 중간 반올림이 없어요. 그래서 결과가 약간 더 정확해요. 매트멀에서 누적이 많이 일어나니까 이 정밀도 차이가 의외로 영향이 있어요. ggml과 Lumen이 비트 단위로 정확히 같은 결과가 안 나오는 이유 중 하나도 이거 (둘 다 FMA 쓰지만 명령 순서가 다름).

Horizontal reduce – 8 lane 정산하기

vfmadd231ps를 112번 반복하면 ymm0에 8개의 partial sum이 들어 있어요. 각 lane이 “전체 sum의 1/8″을 담고 있는 셈입니다. 이 8개를 하나로 합쳐야 최종 결과가 돼요.

이 합치는 단계를 horizontal reduce라고 합니다. 도시 비유로 풀면 — 8차선 도로 끝에 도착한 트럭 8대의 자재를 한 창고에 합산하는 정산 단계. AVX2에서 어셈블리는 좀 복잡해요.

vhaddps ymm0, ymm0, ymm0     # 옆 lane 끼리 짝지어 더하기
vhaddps ymm0, ymm0, ymm0
vextractf128 xmm1, ymm0, 1   # 위 128비트를 xmm1으로
vaddss xmm0, xmm0, xmm1      # 두 결과 합치기

이게 끝나면 xmm0의 첫 lane에 최종 합산 결과가 들어 있어요. 이걸 out[k] 위치에 저장하면 한 column 완료.

복잡해 보이지만 4~5개 명령으로 끝나요. 한 column당 한 번만 하니까 inner loop에 비하면 작은 비용입니다.

이거 처음 짤 때 진짜 헤맸어요. vhaddps가 어떻게 동작하는지 인덱스가 잘 안 잡혔거든요. 256비트 ymm 안에 8개 float이 있는데, vhaddps ymm0, ymm0, ymm0 후 어느 lane에 어떤 값이 들어가는지 매뉴얼 한참 봤어요. 결국 8개를 4개로 줄이고, 4개를 2개로 줄이고, 2개를 1개로 줄이는 트리 패턴인데 첫 commit에서는 인덱스 오답으로 결과가 약간 틀리더라구요. naive 구현과 비트 비교로 잡았어요.

복잡한 하이웨이 인터체인지 항공 뷰

4-Accumulator 패턴 – 정산 창고 4개로 분산

SIMD 매트멀을 짜면 한 가지 함정이 있어요. 같은 accumulator(ymm0)에 계속 더하면 FMA 명령이 직렬로 묶입니다.

vfmadd231ps ymm0, ymm1, ...   # 결과 ymm0 ← 4 사이클 대기
vfmadd231ps ymm0, ymm2, ...   # 앞 결과 기다림 ← 4 사이클 대기
vfmadd231ps ymm0, ymm3, ...   # 또 기다림

FMA 한 번이 4 사이클 걸리는데, 다음 FMA가 같은 ymm0를 사용하니 이전 결과를 기다려야 해요. 4 사이클 × N개 FMA = 4N 사이클. CPU의 FMA 처리량을 활용 못 함.

도시 비유로 풀면 — 8차선 도로에 트럭 8대가 동시에 다닐 수는 있는데, 도착해서 같은 정산 창고에 자재를 합산하는 단계에서 한 줄로 서야 해요. 8대가 정산 창고 앞에서 한 대씩 차례를 기다리면 결국 1차선 정산이라 도로 8차선이 의미 줄어듭니다.

해결책: 4개의 독립 accumulator를 동시에 사용.

vxorps ymm0, ymm0, ymm0     # 4개 모두 0으로 초기화
vxorps ymm1, ymm1, ymm1
vxorps ymm2, ymm2, ymm2
vxorps ymm3, ymm3, ymm3

# 32개 K 원소를 4 chunks (각 8개씩)로 나눠서 각 chunk가 다른 acc 사용
loop:
    vfmadd231ps ymm0, ..., [a + (i+0)*4]   # chunk 0 → ymm0
    vfmadd231ps ymm1, ..., [a + (i+8)*4]   # chunk 1 → ymm1
    vfmadd231ps ymm2, ..., [a + (i+16)*4]  # chunk 2 → ymm2
    vfmadd231ps ymm3, ..., [a + (i+24)*4]  # chunk 3 → ymm3
    # 4개 FMA가 서로 독립이라 병렬 issue 가능
    ...

# 마지막에 합치기
vaddps ymm0, ymm0, ymm1
vaddps ymm2, ymm2, ymm3
vaddps ymm0, ymm0, ymm2
# horizontal reduce

CPU가 4개 FMA를 거의 동시에 시작할 수 있어서 처리량이 약 2배 더 빨라집니다.

도시 비유로 풀면 — 정산 창고를 4개 만들어서 트럭 4대가 동시에 정산하는 거예요. 각 트럭은 다른 정산 창고로 가니까 줄 서기 없어요. 마지막에 4개 창고의 결과를 한 번만 합산하면 끝.

Lumen에서 이 패턴이 Phase 7.G에서 적용됐고, 매트멀 성능이 약 2배 가속됐어요. 정확히는 17.97 → 29.10 tok/s. 이게 사이드 4주차 중반쯤이었는데, 단순한 코드 변경(acc 하나 → 4개)만으로 가속이 그 정도 나왔을 때 진짜 짜릿했어요. 책에서 본 “instruction-level parallelism”이라는 개념이 측정으로 확인된 순간이었거든요.

왜 4개일까

이론적으로는 더 많은 accumulator를 쓸수록 의존성이 더 끊깁니다. 그런데 ymm 레지스터는 16개로 제한돼 있어요. 일부는 매트멀 입력, 임시 변수 등 다른 용도로 써야 하므로 accumulator로 쓸 수 있는 건 4~8개 정도예요.

또 FMA의 latency가 4 사이클이라 4개 acc면 그 의존성이 거의 다 끊깁니다. 더 늘려도 큰 추가 가속 없어요. 4가 sweet spot이라는 게 7.G 측정에서 확인됐어요. 8개까지 늘려본 측정도 했는데, 가속이 +0.1배 정도라 노이즈 수준. 4개가 진짜 적정점이더라구요.

참고로 ggml(llama.cpp)도 4-acc 패턴을 씁니다. 이게 SIMD 매트멀의 표준 패턴이에요. 책에 적혀 있는 패턴이긴 한데, 직접 짜고 측정해 보지 않으면 그 +2배 가속이 진짜인지 의심돼요. 측정으로 확인하면 그제서야 “아 책이 맞았네” 싶어져요.

AVX-512가 항상 더 빠르지는 않다 – Zen 4 일화

상식적으로 AVX-512 (ZMM, 16-lane)가 AVX2 (YMM, 8-lane)의 2배 빨라야 할 것 같죠. 같은 일을 두 배 빨리 하니까요. 16차선 도로가 8차선 도로보다 2배 처리량. 실제로 일부 Intel CPU(Sapphire Rapids 등)에서는 그런 패턴이 나옵니다.

그런데 AMD Zen 4에서는 AVX-512가 오히려 4.5% 느렸어요. 이게 Lumen v0.5 cycle의 Phase 7.R/S/T 측정에서 발견된 사실입니다. 2편에서 잠깐 언급한 일화의 디테일.

이유: AMD Zen 4가 AVX-512를 내부적으로 256비트 두 번으로 쪼개서 실행하는 “double-pumped” 구현이에요. ZMM 명령이 두 µop(micro-operation)으로 쪼개져서 사실상 YMM 명령 두 번과 같습니다. 처리량은 같은데 인코딩 오버헤드와 의존성 체인이 길어져서 오히려 손해예요.

도시 비유로 풀면 — Zen 4의 “16차선 도로”는 사실 8차선 도로 두 개를 신호등으로 번갈아 쓰는 구조예요. 신호등 처리 시간 + 차선 변경 오버헤드 때문에 진짜 16차선 도로가 아닙니다. 청사진에는 16차선이라 적혀 있지만 실제 시공은 8차선 × 2 신호등이라 진짜 처리량은 8차선 한 개와 같은데 신호등 오버헤드만 추가.

이 사실은 AMD 문서 깊은 곳에 적혀 있긴 한데, 우리가 직접 짜고 측정하기 전에는 페이지를 펴 볼 이유가 없었어요. 2편에서 다룬 “직접 짜본 지식”이 정확히 이런 거예요. 책 100번 읽어도 안 보이고 측정해서 부딪쳐야 보이는 사실.

저는 이 측정을 사이드 5주차에 했어요. AVX-512 ZMM 인코더 5일 들여 짠 다음 측정했더니 baseline(YMM) 대비 -4.5%. 처음엔 “내가 어디 잘못 짠 거 아냐?” 의심하다가, 두 인코더의 출력 바이트가 정확히 ZMM 명령과 YMM 명령으로 다른 거 확인. 그래도 측정은 같았어요. 그제서야 AMD 문서 펴서 double-pumped 발견. 5일 작업이 net-neutral이었지만 한 가지 교훈을 얻었어요. “직관은 자주 틀린다” — 이게 9편 측정-주도 의사결정에서 본격적으로 풀 주제예요.

그래서 Lumen은 AVX-512 인프라를 다 갖춰 두고도 default-off 상태로 운영해요. Intel Sapphire Rapids 같은 native 512비트 silicon에서 측정해서 win이 확인되면 한 줄 변경으로 활성화 가능. 인프라 폐기 안 한 이유가 2편의 “다른 호스트에서 살아날 수 있다”는 패턴이에요.

SIMD를 직접 짜야 하나, 컴파일러가 해주나

마지막 의문 하나. Rust나 C 컴파일러는 자동으로 SIMD를 쓸까요? 일부는 그래요. 자동 벡터화(auto-vectorization)라고 부르는 컴파일러 최적화가 단순한 loop을 SIMD로 자동 변환합니다.

let mut acc = 0.0f32;
for i in 0..n {
    acc += a[i] * b[i];
}

이런 직선적인 loop은 LLVM이 자동으로 SIMD 변환 해줘요. 그래서 Rust release 빌드의 naive 매트멀이 이미 어느 정도 SIMD 활용 중입니다.

문제는 더 복잡한 패턴에서 자동 벡터화가 잘 안 되는 거예요.

  • 4-accumulator 패턴: 컴파일러가 의존성 체인 끊기 위해 4개 acc 쓰는 걸 자동으로 알기 어렵습니다.
  • 양자화 매트멀 (Q8 unpack + dequant + multiply): 너무 복잡해서 일반 컴파일러는 최적화 못 합니다.
  • 레지스터 tile (4×8 등): 컴파일러의 일반적 자동 벡터화는 단순 loop만 봐요. 레지스터 tile은 사람이 직접 패턴을 만들어줘야 해요.

도시 비유로 풀면 — 자동 도로 확장 시스템은 직선 시골길을 8차선으로 자동 확장 가능. 그런데 복잡한 도시 교차로, 다층 인터체인지, 정산 창고 4개 합치기 같은 건 사람이 직접 설계해야 해요.

그래서 정말로 빠른 매트멀은 직접 인코딩해야 합니다. 사람이 머리로 패턴을 짜고 어셈블리 바이트를 손으로(또는 codegen으로) 만드는 게 가장 빨라요. Lumen은 이 부분을 codegen으로 자동 합성하니까 “사람이 한 번 짜면 형상별로 자동 적용”이 됩니다.

이게 사이드 시작 전에 가장 헷갈렸던 부분이에요. “그냥 Rust로 짜면 자동 벡터화 잘 해주지 않을까?” 싶었거든요. 실제로 단순 loop은 그래요. 근데 매트멀의 inner loop은 단순해 보여도 컴파일러가 잡지 못하는 디테일이 있어요. 메모리 접근 패턴, accumulator 의존성, instruction scheduling 등. 직접 어셈블리 짜서 측정해 보면 자동 벡터화 결과보다 1.5~2배 정도 더 빨라요. 그 차이가 사이드의 의미였어요.

자동 벡터화 vs 수동 인코딩 측정

참고로 Lumen 첫 commit에서 naive Rust matmul을 측정했더니 8.7배 SIMD 가속 (Phase 3, AVX2 자동 합성 후). 그 다음 4-acc 패턴 + register tile 4×8 적용한 7.G에서 약 19배 가속. 즉 자동 벡터화 대비 사람이 짠 게 2.2배 더 빨랐어요. 단순한 loop은 자동 벡터화도 충분하지만, 매트멀 같은 hot path는 직접 짜는 게 가치가 있어요.

5편 정리

  1. SIMD는 한 명령으로 여러 데이터를 동시 처리하는 기술. AVX2 YMM은 한 명령에 float 8개 (8차선 도로), AVX-512 ZMM은 16개 (16차선). 매트멀처럼 같은 연산을 반복할 때 8배 가속.
  2. FMA (Fused Multiply-Add)는 곱셈+덧셈 한 명령. SIMD와 결합하면 한 명령에 8쌍 곱셈+덧셈이 가능해서 매트멀의 핵심 명령. “자재 트럭에 결합제도 같이 싣기”.
  3. 4-Accumulator 패턴은 FMA 의존성을 끊는 기법. 4개 독립 acc를 번갈아 쓰면 처리량 약 2배 더 가속. “정산 창고 4개로 분산”.
  4. AVX-512가 항상 더 빠르지는 않다. AMD Zen 4는 double-pumped 구현이라 ZMM이 YMM 두 번과 같음. “청사진에는 16차선이지만 시공은 8차선 × 2 신호등”. 호스트마다 측정해야 안다.
  5. 자동 벡터화는 단순한 loop만 처리. 진짜 빠른 매트멀은 직접 인코딩 필요. Lumen은 이걸 codegen으로 자동 합성. 복잡한 다층 인터체인지는 사람이 설계해야.

SIMD가 시리즈에서 가장 코드 디테일이 많은 영역인데, 도로 비유로 풀면 머리에 잘 들어와요. 8차선 도로 + 정산 창고 4개 분산 + 신호등 오버헤드 — 이 그림 하나로 SIMD의 90%가 설명돼요.

다음 편 미리보기

6편은 양자화 (Quantization)를 다룹니다. 모델 weight를 32비트 float에서 8비트 정수로 압축하면 메모리는 4배 줄어드는데 답이 똑같다는 게 어떻게 가능한지. 도시로 풀면 “건물 청사진의 정밀도를 mm 단위에서 cm 단위로 줄여도 시공 결과는 같다”는 비유예요. Q8과 Q4의 차이, fused matmul, VNNI 명령어까지.

5편에서 다룬 SIMD가 매트멀의 연산 속도를 8배 빠르게 했다면, 6편 양자화는 매트멀에 들어가는 데이터 양을 4배 줄여요. 두 기법이 곱해져서 32배 효과가 나오는 거예요. 이게 LLM 추론을 일반 CPU에서 가능하게 만든 핵심 두 기법이에요.

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

그럼 다음 편에서 뵐게요. SIMD나 AVX 관련 질문 있으시면 댓글 남겨주세요.

댓글 달기

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

위로 스크롤