1P by GN⁺ | ★ favorite | 댓글 1개
  • 2013년형 듀얼 Xeon E5-2690 v2와 DDR3로 구성된 서버에서 Gemma 4 26B-A4B Q8_0을 CPU만으로 구동해 디코딩 약 5.2토큰/초, 프롬프트 평가 약 16토큰/초를 달성함
  • ik_llama.cpp의 고속 경로는 AVX2·FMA3를 전제로 하지만 Ivy Bridge CPU는 AVX1만 지원하므로, 비 AVX2 빌드를 위한 컴파일 수정과 연산 폴백이 필요했음
  • 그래프 빌더가 MOE_FUSED_UP_GATEFUSED_UP_GATE를 무조건 생성하는 반면 비 AVX2 디스패처에는 처리 경로가 없어, 순전파마다 약 240개 텐서가 계산되지 않은 채 난삽한 다국어 출력으로 이어짐
  • Claude가 로짓 계측과 코드 분석으로 오류를 찾아 융합 연산을 두 번의 ggml_mul_mat_idggml_fused_mul_unary로 대체했으며, 사용자는 실험을 실행하고 정상 결과의 기준을 판별함
  • 오래된 기업용 서버도 유료 API 장애 시의 로컬 대체 수단이나 느린 배치 작업에 활용할 수 있지만, AVX2 전용 레이아웃을 만드는 --run-time-repack은 사용하면 안 됨

13년 된 스토리지 서버의 실행 환경

  • 재활용한 HP StoreVirtual 장비는 2013년형 듀얼 Xeon E5-2690 v2와 DDR3 메모리를 사용하며 GPU가 없음
    • Ivy Bridge 세대라 AVX1만 지원하고 AVX2와 FMA3는 지원하지 않음
    • 원래 디스크 저장용으로 제작됐으며 구입 비용은 300달러 미만임
  • 실행 모델은 Google의 Gemma 4 26B-A4B로, 260억 파라미터를 가진 오픈 가중치 전문가 혼합(MoE) 모델임
  • Q8_0 양자화 모델에서 디코딩은 약 5.2토큰/초, 프롬프트 평가는 약 16토큰/초로 측정됨

출발점이 된 2016년형 Xeon 사례

  • Hacker News에서 공유된 A 10 year old Xeon is all you need는 GPU 없이 2016년형 단일 Xeon과 128GB DDR3에서 Gemma 4를 실행한 사례임
  • 해당 구성은 ik_llama.cpp와 약 25개의 세밀한 실행 플래그를 사용함
    • 추측 디코딩
    • CPU를 고려한 MoE 라우팅
    • CPU용 플래시 어텐션
    • 실행 시간 가중치 재패킹
  • 같은 접근을 Ivy Bridge 서버에 적용했지만 시작 단계에서 실행이 중단됨
    • 2016년형 Broadwell CPU와 달리 E5-2690 v2에는 AVX2와 FMA3가 없음
    • 이 명령어 집합은 2014년 Haswell, 즉 Intel의 v3 세대부터 제공됐으며 고속 커널은 이를 전제로 작성돼 있었음

Claude를 이용한 진단과 패치

  • 시작 실패 정보를 전달받은 Claude는 CPU 명령어 집합 차이를 원인으로 특정함
  • 앞서 무료 모델로 시도했던 미완성 접근을 이어받아, 성능상 중요한 C++ 경로가 AVX2 이전 CPU에서 올바르게 폴백하도록 수정함
  • 작업은 단일 fix it 요청으로 끝나지 않았음
    • 다른 개발자가 작성한 성능 중심 C++ 코드를 읽음
    • 특정 마이크로아키텍처에서 커널이 유효하지 않은 이유를 분석함
    • 포크의 기존 최적화를 버리지 않으면서 지원되지 않는 경로를 우회함
  • 사용자는 C++ 커널 폴백을 직접 작성하는 대신 실험을 실행하고 출력을 읽으며 다음 질문을 정하고, 정상 결과의 기준을 판별하는 역할을 맡음
  • 진단과 패치는 해당 서버에서 실행된 Claude 인스턴스가 수행함

비 AVX2 빌드에서 깨진 연산 경로

  • Gemma 4 MoE 추론에 사용한 ik_llama.cppllama.cpp 포크이며, 기본적으로 AVX2를 최소 조건으로 가정함
  • 빌드할 때 GGML_USE_IQK_MULMAT를 끄면 대부분의 고속 경로가 제외되고 일반 스칼라/SSE 연산으로 폴백함
    • 일반적인 Q8_0 행렬 곱셈에는 이 폴백이 작동함
    • 다만 두 그래프 연산에는 폴백이 적용되지 않았음
  • Gemma 4의 MoE 피드포워드 네트워크는 다음 연산을 생성함
    • MOE_FUSED_UP_GATE: 전문가별 gate·up 행렬 곱셈과 SwiGLU를 결합한 연산
    • FUSED_UP_GATE: 비 MoE 계층에서 사용하는 밀집 연산 버전
  • 컴퓨트 디스패처의 두 연산은 GGML_USE_IQK_MULMAT 조건으로 보호됐지만, 그래프 빌더는 조건 없이 연산을 생성
    • 비 AVX2 빌드의 디스패처에는 해당 enum을 처리하는 case가 없었음
    • 연산이 기본 분기로 빠지면서 모든 전문가 피드포워드 네트워크의 목적지 텐서가 조용히 계산되지 않음
  • Gemma 4 26B는 30개 계층에서 토큰당 활성 전문가 8개를 사용하므로, 순전파마다 메모리 버퍼에 남아 있던 값이 담긴 텐서 약 240개를 소비하게 됨

난삽한 출력에서 드러난 단서

  • 오류 출력은 유창해 보이지만 의미 없는 다국어 문자열이었음
    • 26만 2,000개 어휘 전체에 토큰 ID가 고르게 퍼짐
    • 태국어, 한국어, <unused> 센티널, 영어 조각 등이 비슷한 빈도로 생성됨
  • 온도 0에서 출력은 결정적이었고, 단일 스레드와 다중 스레드 실행 결과도 바이트 단위로 같았으며 NaN은 발생하지 않음
  • 계층마다 큰 상수가 은닉 상태를 밀어 최종 소프트맥스가 평평해지는 형태를 보임
  • Claude는 샘플링 전 원시 로짓을 계측해 상위 5개 토큰, 범위, 평균, NaN 수를 출력함
    • 첫 예측 토큰의 평균 로짓이 0 근처가 아니라 +16이었음
    • 전체 어휘의 약 80%가 양의 로짓을 가졌음
  • 무작위 손상과 달리 편향이 일정했기 때문에, 은닉 상태의 큰 부분이 작은 양의 부동소수점 값이 남은 초기화되지 않은 메모리를 사용한다고 범위를 좁힘

세 단계로 구성된 수정

  • 패치는 포크의 main 위에 세 개 커밋으로 구성됨
  • 비 AVX2 컴파일 수정

    • iqk_quantize.cppquantize_row_q8_0_x4quantize_row_q8_1_x4_T에 있는 스칼라 #else 분기가 실제로는 hsum_i32_8 등 AVX2 도우미를 참조하고 있었음
    • 해당 분기를 이식 가능한 스칼라 루프로 다시 작성함
    • ggml.cggml-quants.c로 새어 나온 일부 IQK 호출에 #if GGML_USE_IQK_MULMAT 보호 조건을 추가함
    • iqk_cpu_ops.cpp가 독립적으로 컴파일되도록 누락된 include를 추가함
    • 이 수정이 없으면 포크 자체가 비 AVX2 하드웨어에서 빌드되지 않음
  • 실행 시간 그래프 폴백

    • 디스패처를 고치는 대신 그래프 빌더가 해당 빌드에서 이미 계산 경로를 가진 연산을 생성하도록 변경함
    • ggml_moe_up_gate에서 GGML_USE_IQK_MULMAT가 꺼져 있을 때 결합된 up_gate_exps 가중치를 처리함
    • 텐서 형태는 [n_embd, 2*n_ff, n_experts]이며 앞 절반은 gate, 뒤 절반은 up임
    • 두 개의 ggml_view_3d 슬라이스로 분리함
    • 각 슬라이스에 ggml_mul_mat_id를 실행함
    • ggml_fused_mul_unary(gate, up, SILU)로 두 결과를 결합함
    • gate와 up 가중치가 이미 분리돼 있다면 슬라이스 없이 두 번의 ggml_mul_mat_id와 융합 곱셈·단항 연산을 사용함
    • 비 MoE 계층에서 사용하는 ggml_fused_up_gate에도 같은 폴백을 적용함
    • mul_mat_id는 기본 ggml 구현을 사용하고 fused_mul_unary는 SILU와 곱셈을 한 번에 처리하므로, 모든 구성 연산에 비 IQK 구현이 이미 존재함
    • 전체 변경은 #if !GGML_USE_IQK_MULMAT 뒤에 있어 AVX2 빌드 결과는 기존과 비트 단위로 같음
  • CI 스텁 정비

    • IQK 소스의 #else 스텁이 iqk_mul_mat.h와 달라 비 AVX2 하드웨어에서 ci/run.sh도 빌드되지 않았음
    • <cstdint> include가 빠져 있었음
    • 일부 스텁은 불필요한 선행 매개변수를 갖거나 sinks 매개변수가 누락되는 등 시그니처가 달랐음
    • 일부 함수는 스텁 자체가 없어 링크 단계에서 정의되지 않은 참조가 발생함
    • 스텁을 헤더와 맞추면서 비 AVX2 환경에서도 테스트 스위트 실행이 가능해짐

폴백의 성능 비용과 재패킹 오류

  • 폴백은 하나의 융합 커널 대신 두 번의 mul_mat_id를 실행하므로 추가 비용이 발생함
  • 해당 CPU는 이미 메모리 대역폭 제약을 받고 있고 원래 융합 커널도 AVX2 전용이어서, 이 환경에서 포기하는 기존 실행 경로는 없었음
  • 최종 성능은 26B-A4B MoE에서 디코딩 약 5.2토큰/초, 프롬프트 평가 약 16토큰/초임
  • --run-time-repack은 시작할 때 양자화 가중치를 AVX2 전용 인터리브 형식인 Q8_0_R8로 재배열함
    • AVX1 환경에서 출력을 다시 손상시키는 별도 버그
    • 현재 패치는 이를 수정하지 않으며 실행 스크립트에서 해당 플래그를 제거함

원인을 좁힌 과정

  • 명령어 집합 불일치는 쉽게 확인됐지만, 디스패처가 오류 없이 기본 분기로 빠지는 문제는 찾기 어려웠음
  • 코드 검토를 통해 여러 후보를 제거함
    • RMSNorm 도우미는 올바르게 보였음
    • ggml_vec_dot_q8_0_q8_0의 AVX1 폴백도 올바르게 보였음
    • 단일 스레드 결과가 비트 단위로 같아 스레딩 오류를 배제함
  • 로짓 평균이 +16에 고정되고 긴 꼬리의 토큰들이 비슷한 값을 가진다는 점에서 잔차 스트림의 큰 부분이 초기화되지 않았다고 판단함
  • 디스패처에서 #if GGML_USE_IQK_MULMAT를 검색한 뒤 약 1분 만에 누락된 두 연산 경로를 찾음

재현 조건과 활용 범위

  • AVX2 이전 장비의 재현 조건은 다음과 같음
    • 하드웨어: 듀얼 Xeon E5-2690 v2, Ivy Bridge, AVX1, DDR3, GPU 없음
    • 빌드: 패치 브랜치의 ik_llama.cppGGML_USE_IQK_MULMAT 없이 컴파일
    • 모델: Gemma 4 26B-A4B Q8_0
    • 실행: 일반적인 ik_llama.cpp CPU 플래그를 사용하되 --run-time-repack은 제외
  • 정확한 변경은 ikawrakow/ik_llama.cpp PR #2138에서 확인할 수 있음
    • 작성 시점에는 아직 열려 있고 관리자 검토를 기다리는 상태라 브랜치에서 직접 실행해야 함
    • 같은 장비에서 발생한 버그는 PR 스레드에 보고할 수 있음
  • 오래된 기업용 서버에 로컬 모델을 유지하면 유료 API가 중단됐을 때 대체 수단으로 쓰거나, 토큰당 과금이 맞지 않는 느린 배치 작업을 처리할 수 있음
  • 구독 서비스 이용보다 낯선 코드와 오래된 시스템을 직접 파고드는 능력을 중시하며, 이는 15년 된 Rails 애플리케이션이나 담당자가 떠난 데이터베이스를 유지하는 작업에도 같은 방식으로 적용됨

댓글과 토론

Hacker News 의견들
  • 2027년 중반이면 2,000억 개 초과 전문가 혼합(MoE) 모델을 일반 소비자용 하드웨어에서 구동할 수 있으리라 예상함
    16GB Mac에서 Qwen3.6-35B-A3B를 로컬로 돌리며 초당 7~9토큰을 얻고 있음: https://github.com/deepanwadhwa/samosa-chat
    16GB RAM MacBook Air에서 GPT-4급 모델이 이 정도 속도로 로컬 실행되는 셈임

    • 그때까지 기다릴 필요도 없을 듯함. Prism이 며칠 전 공개한 3진 가중치 모델 Bonsai 27B는 약 7GB에 불과하고 M4 Max 노트북에서 초당 44토큰 이상을 냄: https://huggingface.co/prism-ml/Ternary-Bonsai-27B-mlx-2bit
      이미 대다수 2,000억 개 이상 모델의 활성 매개변수 수와 비슷한 범위라 Prism이 마음만 먹으면 그런 모델을 내놓을 수 있음. 다만 HRM 같은 재귀 신경망은 매개변수가 그만큼 많이 필요하지 않으므로, 정말 그런 규모가 필요한지는 논쟁의 여지가 있음: https://huggingface.co/sapientinc/HRM-Text-1B
    • 초당 7~9토큰은 괜찮은 속도라 보기 어려움. 전문가까지는 아니지만 로컬에서 실험해 보니 초당 12~16토큰보다 느리면 답답했음
    • 예측에는 대체로 동의하지만 시점은 조금 더 늦을 수 있다고 봄. 이런 성과를 내려면 현재처럼 매개변수당 비트 수를 줄이는 것과는 전혀 다른 압축이 필요하므로, 그 상황에서는 기존의 매개변수 개수라는 척도 자체가 의미 없을 수 있음
      가령 계층마다 256비트 시드 하나만 두고 이를 잡음 함수에 넣어 16K개의 실제 가중치를 생성하면, 가중치 하나당 저장 크기를 1비트 미만으로 낮출 수 있음
    • Ornith 35B를 Pi로 돌려 초당 50토큰 이상을 얻고 있음. Pi가 검색과 가져오기도 지원한다는 걸 안 뒤로는 검색용으로 대형 모델을 찾을 필요가 없어졌음
      이번 달과 다음 달에 판도를 바꿀 새 모델들이 연달아 나올 듯해 기대됨. Ornith는 꼭 써볼 만함
    • 초당 9토큰이면 견디기 어렵지 않나 싶음. Claude 세션에서 하루도 안 돼 100만 토큰을 쓰는 일이 흔한데, 실제 작업에는 너무 느려 보임
  • 듣기 싫어하는 사람도 있겠지만, 추론 제공업체의 토큰 비용이 로컬 구동 전기료보다 저렴하다고 봄
    단순화를 위해 출력 생성만 계산하면 초당 5토큰은 시간당 18,000토큰이며, 제공업체 비용은 약 0.005달러임. 서버가 추론 중 약 500W를 쓴다고 보고 전력 요금이 kWh당 0.3달러인 독일을 기준으로 하면, 같은 양을 로컬에서 생성하는 데 0.15달러가 들어 30배 비쌈
    개인정보가 걱정된다면 로컬 실행이 여전히 좋지만, 추론 제공업체보다 훨씬 비효율적이라는 점은 알아야 함. 새 GPU가 추론 효율을 높이면서 이 격차도 크게 벌어질 것임
    처음에는 18만 토큰으로 잘못 계산했지만 실제로는 18,000토큰이므로, 전기를 거의 공짜로 쓰지 않는 한 경쟁하기 어려움. 제공업체들도 소형 모델에 아직 H200/H100을 쓸 텐데 GB300이나 내년 Ruby GPU로 바꾸면 추론비가 30분의 1로 내려가 로컬 모델의 주된 가치는 개인정보 보호가 될 듯함

    • 집에서 Qwen 27B를 구동하면 작업 중 약 400W를 소비하고, 생성은 초당 약 40토큰이며 더 중요한 프롬프트 처리는 초당 약 1,000토큰임
      한 시간이면 360만 입력 토큰을 처리하거나 144,000토큰을 생성하며 전기료는 약 0.15달러임. Sonnet으로 같은 양을 처리하면 입력은 7.2달러, 생성은 1.4달러이므로 클라우드가 생성에서 10배, 처리에서는 거의 50배 비쌈
    • 미국에서는 kWh당 0.30달러에 근접하지도 않음. 사용량이 적은 시간대에는 그 절반이고, 배터리에 1,000달러를 투자해 초저가 시간대의 0.11달러 전기를 저장할 수도 있음
      추론 제공업체들은 막대한 부채를 안고 시장점유율 경쟁 중이므로 가격은 분명 오를 것임
    • 태양광과 배터리로 작동하는 독립형 자가 호스팅 LLM 서버를 만들면 멋질 듯함. Low Tech Magazine처럼 햇빛이 부족할 때는 오프라인이 되고, 친구 모임·거리·동호회 등 지역 공동체가 매일 쓰기에 필요한 규모로 구축하는 방식임
      AI를 공동체가 직접 통제할 만큼 민주화하면 데이터센터 문제도 풀고, 검열과 정렬 수준도 공동체가 민주적으로 정할 수 있음. Geohotz의 일부 글과 비슷한 구상임
      오픈소스 모델은 곧 중고 하드웨어로 저렴하게 서비스할 만큼 좋아지고 효율적이 될 것임. 작은 공동체마다 기술에 밝은 사람 한 명이 수백 달러의 초기 비용을 모으면 이후에는 전기료 없이 거의 무료로 운영할 수 있음
      https://solar.lowtechmagazine.com/
    • 효율도 중요하지만 전기가 저렴하거나 효율적인 장비를 가진 사람도 많음. AMD Strix Halo 홈 서버에서는 100W만 사용하면서 Gemma4-26B를 대략 초당 70토큰으로 제공할 수 있음
    • 비용이 더 드는지는 신경 쓰지 않음. 로컬로 돌리는 이유는 돈을 아끼기 위해서가 아니라 자유와 개인정보 보호를 얻기 위해서이며, 선택지가 존재하면 장기적으로 비용도 낮아짐
      로컬이라는 대안이 없다면 클라우드 모델 가격은 훨씬 비쌀 것임
  • 이 세대의 듀얼 Xeon은 부하가 걸리면 300W 이상을 소비할 가능성이 큼. 미국 평균 전기료로 하루 1.35달러이며, 여름에 공간을 냉방해야 한다면 더 들어감
    프롬프트 처리 시간을 무시하고 24시간 돌려도 하루 약 40만 토큰이므로 출력 100만 토큰당 약 0.30달러임. 공교롭게도 현재 OpenRouter에서 이 모델의 가격과 같지만, OpenRouter의 생성 속도는 8배 빠름
    데이터가 집 밖으로 나가지 않게 하는 등 로컬 LLM을 실험할 이유는 많지만 금전적으로 이득 보기는 어려움. 집의 로컬 추론 장비에 훨씬 많은 돈을 투자한 입장에서, 재미는 있어도 절약 수단은 아니라고 봄

    • 실제 주택에 사는 사람이라면 합리적인 계산임. 나는 임대 주택에 살며 전기료를 직접 내지 않으므로 집주인이 항의하기 시작하는 시점이 비용 효율의 경계임
    • 대부분은 돈을 아끼려고 모델을 로컬에서 구동하는 게 아니라 개인정보를 퍼뜨리지 않기 위해서라고 봄
    • 암호화폐와 비슷함. 전력 요금 때문에 대다수에게는 직접 채굴하는 것보다 구매하는 편이 저렴함
    • 집에서 난방을 켜는 추운 계절에는 폐열도 쓸 수 있어 경제성이 더 좋아짐
  • GPU 없이 듀얼 Xeon과 DDR4 256GB에서 여러 모델을 구동한 결과를 정리했음
    https://gist.github.com/hparadiz/f3596d00a62d8ebb2dadcc46ee5822c7

    • NUMA 성능 저하를 없애기 위해 CPU 하나만 사용해 봤는지 궁금함. 메모리를 절반만 쓰게 되겠지만 성능 차이가 얼마나 나는지 알고 싶음
    • 느린 RAM이 아주 많으면 소형 모델도 상당히 빠르게 도는 듯함. 이 장비에서 더 큰 모델은 어떻게 작동할지 궁금함
  • 꽤 느린 편임. 13년 된 CPU에서도 초당 8~12토큰을 얻고 있으며, 문맥 크기와 다른 설정에 따라 달라질 수 있음
    https://news.ycombinator.com/item?id=48354801

    • Xeon E3-1270 V2 3.50GHz와 오래된 Nvidia Quadro K2200 4GB에서도 초당 8~9토큰이 나옴. Ollama에서 gemma4:e2b와 gemma4:12b-it-qat를 구동하고 있음
    • 원 글은 Q8 양자화를 쓰고 여기서는 Q4를 쓰는 차이가 있지 않나?
  • 원 글 작성자임. 원래 댓글이 어떤 이유로 신고 처리된 듯함. 수정 사항은 상위 프로젝트의 PR #2138로 열어뒀음: https://github.com/ikawrakow/ik_llama.cpp/pull/2138

  • StoreVirtual 장비에는 소프트웨어를 설치하는 데 쓸 만한 포트가 없어 보임. USB 포트 정도만 있는데, 직렬 콘솔을 통해 설치하는 것인지 궁금함

  • 같은 구성으로 2013 Mac Pro에서 Gemma 4 26B를 돌리며 초당 약 5토큰을 얻고 있음. 듀얼 그래픽 카드는 여기에는 쓸모없지만 일부 작업에는 충분히 실용적임

  • 지하실의 구성을 직접 봤는데 정말 굉장함. 다음에는 3D 프린터도 소개해 주면 좋겠음

  • 다소 관련 있는 영상으로, Pentium 4에서 LLM을 구동하며 NetburstGPT라는 별명을 붙였음. 물론 매우 느림
    https://www.youtube.com/watch?v=ILV-eu90te8