RicoCheese기술 뉴스와 기록
← 목록으로
뉴스2026-09-1613분

4GB 노트북 GPU가 12코어 CPU를 4.3배 앞질렀다 — Gemma 4 실측 기록

llama.cpp로 Gemma 4 E2B q4_0을 같은 노트북에서 두 번 돌렸다. CPU만 쓴 쪽과 2021년형 4GB GTX 1650 Ti를 쓴 쪽의 차이는 플래그 하나뿐이었고, 디코딩 속도는 4.3배 벌어졌다.

#machinelearning#gpu#benchmarking#python
A 4 GB Laptop GPU Beats a 12-Core CPU by 4.3x on Gemma 4

개요 #

노트북 한 대로 같은 모델을 두 번 돌린 실험 결과가 공개됐다. 한 번은 CPU만 쓰고, 한 번은 같은 섀시에 박혀 있는 4GB GTX 1650 Ti를 썼다. 모델 파일은 바이트 단위로 동일했고, 명령줄에서 다른 건 플래그 하나였다. 디코딩 속도는 GPU 쪽이 4.3배 빨랐다.

테스트 대상은 google/gemma-4-E2B-it-qat-q4_0-gguf, 3.35GB짜리 양자화 GGUF다. 실험 코드는 github.com/xbill9/gemma4-dev에 공개돼 있다.

눈길이 가는 대목은 속도보다 메모리 쪽이다. 3.35GB 모델을 4GB 카드에 올렸는데, 실제로 카드가 물고 있던 건 1598MiB였다.

무엇과 무엇을 비교했나 #

머신은 13세대 인텔 코어 i7-1360P 노트북 한 대. 여기에 GTX 1650 Ti Max-Q가 들어 있다.

CPU 쪽GPU 쪽
장치i7-1360P, 12코어 / 16스레드GTX 1650 Ti Max-Q, 4096MiB
구성SMT P코어 4개(0-7) + E코어 8개(8-15)TU117, compute capability 7.5, 텐서 코어 없음
SIMD / 연산avx2, avx_vnni, AVX-512 없음CUDA
llama.cppc6824a9, GGML_CUDA=OFF 빌드c6824a9, CUDA 빌드
차이나는 플래그-ngl 0-ngl 99

나머지는 전부 같다. 사실 이 실험의 전부가 그거다.

shell
-m gemma-4-E2B_q4_0-it.gguf --host 127.0.0.1 --port 8080 \
  -ngl {0|99} -c 8192 -ctk f16 -ctv f16 -fa 1 -t 4 -tb 8 --parallel 1 --metrics

두 쪽을 같은 엔드포인트에 번갈아 붙였고, CPU를 먼저 돌린 뒤 120초를 쉬고 GPU로 넘어갔다.

숫자 #

프롬프트 길이 4종 × 출력 길이 2종, 총 8개 셀을 각각 3회씩 반복했다. 동시성은 1. 디코딩 속도는 SSE 스트림에서 클라이언트가 직접 잰 토큰 간 속도인데, 두 쪽 모두에서 뽑을 수 있는 유일한 디코딩 지표라서 그걸 썼다.

입력 tok출력 tokCPU 디코딩GPU 디코딩배수CPU TTFT msGPU TTFT ms배수
943217.6171.224.04x11433852.97x
9412817.2271.204.13x12193853.17x
5163216.1669.934.33x597816103.71x
51612816.3969.494.24x599516123.72x
9983215.8168.604.34x1174832443.62x
99812816.1068.524.26x1153232423.56x
19593215.6667.614.32x2392665223.67x
195912815.8067.644.28x2377365223.65x

8개 셀의 중앙값은 디코딩 4.27배, 프리필 3.63배, 종단 간 3.81배였다.

셀 내부 3회 반복의 편차는 CPU 쪽 최악 6.29%, GPU 쪽 0.84%. 디코딩 배수는 모든 셀에서 4.04배~4.34배 사이에 들어왔다. 노이즈보다 한 자릿수 위라서 보고할 값어치가 있다는 게 저자의 설명이다.

디코딩은 평평하고, 프리필은 비례한다 #

표를 가로가 아니라 세로로 읽어보면 더 흥미로운 게 나온다.

CPU 디코딩은 17.61에서 15.66 tok/s로 떨어진다. GPU는 71.22에서 67.61. 프롬프트 길이가 21배 늘어나는 동안 양쪽 모두 디코딩 속도는 거의 움직이지 않았다. 반면 TTFT는 CPU가 1143ms에서 23926ms로, GPU가 385ms에서 6522ms로 프롬프트 길이에 비례해 늘었다.

디코딩은 토큰마다 모델 전체를 읽으니 대역폭에 묶이고, 프리필은 프롬프트 전체를 곱해 넘기니 연산에 묶인다. 가속기는 둘 다 도와주지만 도와주는 이유가 다르다. 숫자 하나만 인용하는 벤치마크가 가리는 게 바로 이 차이다.

디코딩 배수 자체도 컨텍스트가 길어질수록 올라간다. 94토큰에서 4.04배, 998토큰에서 4.34배. KV 캐시가 커질수록 CPU 쪽이 밀리는데 GPU 쪽은 거의 밀리지 않기 때문이다.

3.35GB가 4GB 카드에 어떻게 들어갔나 #

들어갈 필요가 없었다는 게 답이다. 모델을 올려 서빙 중인 상태에서 카드가 잡고 있던 건 1598MiB다.

파일 전체에서 Q4_0은 32%뿐이다. 임베딩 텐서 두 개가 Q6_K인데, 이 둘이 텐서 바이트 3.334GB 중 2.257GB를 차지한다. 그중 제일 큰 per_layer_token_embd는 1.93GB로 파일의 58%인데, llama.cpp의 src/models/gemma4.cpp에서 TENSOR_READ_LAZY로 만들어지고 GGML_OP_GET_ROWS가 mmap에서 직접 꺼내 쓴다. 토큰당 몇 개 행만 건드린다. 카드로는 아예 올라가지 않는다.

카드에 상주하는 건 약 1.08GB의 Q4_0 트랜스포머 본체 — 디코딩이 토큰마다 읽는 그 부분 — 에 KV 캐시 60MiB 남짓과 연산 버퍼가 전부다.

같은 구조가 CPU 쪽에서는 오히려 손해다. 거기서 RssAnon은 1,202,152kB다. llama.cpp가 AVX2 커널용으로 Q4_0 가중치를 인터리브 레이아웃으로 재배치하면서 본체를 mmap 밖 익명 메모리로 복사하기 때문이다.

이 헤드라인은 카드가 작은데도 나온 게 아니다. 체크포인트의 생김새 덕에 나왔다. 2021년산 4GB GPU는 이 모델에 작지 않다. 오히려 필요량의 2.5배쯤 된다.

무엇을 통제했나 #

두 쪽은 플래그 하나만 다르다. 나머지는 가정하지 않고 하나씩 확인해가며 맞췄다.

항목맞춘 방식
엔진llama.cpp 커밋 c6824a9 하나를 두 번 빌드 — CPU 빌드, CUDA 빌드
바이너리 동일성실행 중인 실행 파일의 SHA-256을 리포트에 기록
플래그-ngl 말고는 동일. 양쪽 다 -t 4 -tb 8
프롬프트하네스 하나, 양쪽 리그에서 바이트 단위 동일, 장치 중립 필러 사용
프롬프트 길이짝지은 셀마다 정확히 일치 — 94, 516, 998, 1959 토큰
엔드포인트같은 127.0.0.1:8080, 한 번에 한 쪽만 구동

마지막 줄이 특히 곱씹을 만하다. 양쪽이 같은 포트에서 같은 모델을 서빙하니 HTTP 응답만 봐서는 어느 장치가 처리했는지 알 수 없다. 그래서 장치를 라벨이 아니라 실행 중인 프로세스에서 읽어냈다. 바이너리는 /proc/<pid>/exe, 실제로 로드된 ggml 백엔드는 /proc/<pid>/maps, 진짜 -ngl 값은 /proc/<pid>/cmdline에서 확인하고, 그 증거를 모든 리포트에 숫자와 나란히 찍었다.

ldd가 아니라 maps를 쓴 이유는 llama.cpp가 백엔드를 dlopen하기 때문이다. CUDA 백엔드가 ldd 출력에는 없는데 실행 중인 프로세스에는 올라와 있을 수 있다. 판정하려면 GPU 백엔드가 매핑돼 있고 동시에 거기에 레이어가 할당돼 있어야 한다. CUDA 빌드를 -ngl 0으로 돌리면 연산은 CPU에서 하지만 깨끗한 CPU 쪽이라고 보기 어렵다. 장치가 초기화된 상태라 큰 프리필 배치는 여전히 거기로 넘어갈 수 있다. 그래서 CPU 쪽은 -ngl 0을 믿는 대신 프로세스에서 장치를 아예 감췄다.

무엇을 통제하지 못했나 #

저자는 4배는 진짜로 받아들이고, 7% 안쪽은 없는 셈 치라고 못 박는다.

  • 순서와 발열. CPU 쪽을 먼저 돌렸고 12코어를 다 태웠다. i7-1360P와 Max-Q 카드는 발열 예산을 공유하므로 GPU 쪽은 데워진 패키지에서 출발했다. 120초 쿨다운은 측정값이 아니라 어림짐작이다. 편향이 있다면 GPU 쪽을 과소평가하는 방향이다.
  • 페이지 캐시. GGUF는 양쪽 모두 캐시가 데워진 상태였다. 콜드 스타트에 대해서는 말해주는 게 없고, 위의 지연 임베딩 이야기도 캐시를 비우고 돌린 실험이 아니라 소스 코드와 상주 메모리 수치에서 추론한 것이다.
  • CPU 어피니티. 아무것도 핀으로 묶지 않았다. 같은 다이에서 돌린 별도 llama-bench 스윕에서는 어피니티만으로 프리필이 1.61배 차이 났다. llama.cpp가 작업을 균등 분배하는데 모든 배리어가 가장 느린 스레드를 기다리는 탓에, P코어 4개에 E코어 8개를 더하면 프리필이 오히려 느려진다. 즉 여기 CPU 쪽은 최상의 상태가 아니고, 프리필 3.63배는 상한값이다. 디코딩은 영향을 받지 않는다. 스레드 수가 디코딩에서는 약한 지렛대라서, 4.27배는 그대로 유효하다.
  • 동시성. 처음부터 끝까지 단일 스트림, --parallel 1.

정리 #

4GB 노트북 GPU가 요즘 12코어 CPU 대비 작은 양자화 모델 서빙에서 얼마나 값을 하는지 재보자는 게 목표였다. 방법은 단일 변수 비교다. llama.cpp 커밋 하나, 프롬프트 세트 하나, 플래그 하나 차이, 그리고 서빙 장치는 런타임에 /proc에서 읽어 모든 숫자 옆에 적었다.

  • GPU 디코딩은 CPU의 4.27배, 전 셀에서 4.04~4.34배
  • GPU 프리필은 3.63배, 다만 CPU 쪽을 핀으로 묶지 않았으므로 상한값
  • 종단 간 3.81배
  • 디코딩은 양쪽 모두 컨텍스트에 평평하고, TTFT는 양쪽 모두 비례
  • 모델이 카드에서 쓴 건 1598MiB. 파일의 58%가 호스트 메모리를 떠나지 않는 지연 임베딩 테이블이라서다

범위는 노트북 한 대, GGUF 하나, 같은 커밋에서 두 번 빌드한 llama.cpp c6824a9, 짝지은 8개 셀, 셀당 3회 반복, 동시성 1, CPU 쪽 선행에 120초 쿨다운. CPU 어피니티는 양쪽 다 설정하지 않았고 페이지 캐시는 양쪽 다 데워진 상태였다.


이 글은 위 출처를 바탕으로 한국 독자를 위해 재작성한 기사입니다. 원문의 사실과 수치에 근거하며, 별도의 견해를 포함하지 않습니다.

댓글GitHub Discussions