구형 제온 서버 174번 재부팅하며 찾아낸 최적 LLM 추론 옵션
10년 된 구형 CPU 환경에서 LLM(Gemma 4)을 빠르게 구동하기 위해 추론 엔진의 25개 명령어 플래그(flags)를 하나씩 제거하며 성능 변화를 측정한 실험 결과입니다. 플래그들은 하드웨어나 작업량에 따라 상호작용하므로 무작정 복사해서 붙여넣기보다 실제 환경에서 테스트해야 함을 보여줍니다. 하드웨어 제약 없이 오픈소스 모델을 최적화하려는 실무자들에게 매우 유용한 가이드입니다.
발행일: 2026년 6월 15일
제목: 초당 4개의 토큰을 얻기 위해 10년 된 제온(Xeon)을 174번 재시작하고 12개의 플래그를 삭제한 이야기
읽는 데 약 22분 소요
이 글은 2016년산 제온에서 Gemma 4를 구동했던 글의 후속 편입니다. 저는 어떤 플래그들이 실제로 효과가 있는지, 어떤 것이 해로운지, 그리고 어떤 것이 그저 함정인지 찾아내기 위해 25개의 플래그로 구성된 기존 설정을 하나씩 분해해 보았습니다. 제가 튜닝한 대부분의 설정은 일반 사용자에게는 아무런 도움이 되지 않을 것입니다.
몇 주 전, 저는 GPU 없이 128GB DDR3 메모리가 탑재된 2016년형 제온(Xeon)에서 260억 개의 매개변수를 가진 모델인 Gemma 4를 읽는 속도로 구동하는 방법을 공유했습니다. 그 게시물은 해커뉴스(Hacker News) 첫 페이지에 약 8시간 동안 게재되었는데, 이는 많은 사람들이 블로그에서 복사한 25개의 플래그가 포함된 명령어를 자신의 터미널 어딘가에 그대로 붙여넣고 있으며, 정작 그 플래그들이 각각 어떤 역할을 하는지 전혀 모르고 있다는 것을 의미합니다. 해당 명령어에 관해 안 좋은 소식이 있습니다. 여러분은 아마도 그 명령어를 잘못된 방식으로 사용하고 있을 것입니다.
제가 지난 게시물에서 애석하게 전했듯, 이 플래그들의 절반은 단순히 명령어에 적어둔다고 해서 작동하지 않습니다. 일부는 올바른 하드웨어가 필요하고, 일부는 올바른 호스트 설정이 필요하며, 또 일부는 특정 작업량(workload)에서만 도움이 됩니다. 엔진은 이 모든 플래그를 오류 없이 받아들이지만, 정작 어떤 플래그가 실제로 적용되었는지에 대해서는 거의 아무런 정보도 알려주지 않습니다. 그래서 이 글에서는 여러분의 환경에서 실제로 어떤 것이 작동할지 다시 추적해 보았습니다.
이를 확인하는 방법은 절제(ablation) 테스트입니다. 작동하는 설정에서 정확히 하나의 플래그를 끄고, 어떤 변화가 있는지 측정한 다음, 다시 켜놓고 모든 플래그에 대해 차례로 동일한 작업을 반복하는 것입니다. '절제'라는 단어는 신경과학을 거쳐 머신러닝으로 차용된 용어로, 일반적으로 모델 자체의 일부(어텐션 헤드나 레이어 등)를 제거하여 그 기능이 무엇인지 알아보는 것을 의미합니다. 여기서는 모델 내부가 아닌 추론 플래그에 사용한 약간 부적절한 표현이지만, 아이디어 자체는 동일합니다. 하나를 끄고, 측정하고, 반복하여 그 차이를 통해 각 부분의 가치를 알아내는 것입니다.
이 과정은 엄청난 노동이었습니다. 정말 엄청나게요. 174번의 실행을 했으며, 매번 서버를 재시작하면서 토큰을 단 하나 생성하기도 전에 25GB에 달하는 가중치(weights)를 회전하는 디스크(스핀들 디스크)에서 다시 불러와야 했습니다. 실행 당 3개의 테스트 프롬프트를 여러 번 반복했으며, 나중에 언급할 교착 상태(deadlock) 때문에 밤샘 작업 한 번의 결과를 통째로 버려야 했습니다. 제가 이 횟수를 강조하는 이유는, 횟수 자체가 핵심이기 때문입니다. 아무도 이 플래그들 중 무엇이 중요한지 모르는 이유는, 알아내는 과정이 너무 느리고 지루해서 아무도 이런 테스트를 하지 않기 때문입니다. 제가 해냈기 때문에 이 글에서 그 정답을 공유합니다.
테스트 설정
장비는 제온 포스트에서 사용한 것과 동일합니다. 제온 E5-2620 v4, 8개의 물리적 코어, 16개의 스레드, 128GB DDR3 메모리, GPU 및 스왑(swap) 없음. 엔진은 ik_llama.cpp의 feat/gemma-4-mtp 브랜치를 사용했습니다. 검증기(verifier)는 Q8_0 포맷의 gemma-4-26B-A4B-it이며, 이와 짝을 이루는 Q8_0 포맷의 MTP 드래프터(drafter)를 사용했습니다.
저는 3가지 테스트 프롬프트를 사용할 것입니다:
- 간단한 채팅
- 요약할 약 5,000 토큰 분량의 문서
- 코드 생성 요청
탐욕적 디코딩(greedy decoding), 고정된 시드(seed), 새 토큰 256개, 3번의 반복 측정을 진행했으며 중간값을 보고합니다.
수치를 살펴보기 전에 짚고 넘어갈 것이 두 가지 있습니다. 먼저, 저는 원래 명령어의 llama-cli가 아닌 llama-server 환경에서 벤치마크를 실행했습니다. 서버에는 요청별 '투기적 디코딩(speculative-decoding)' 원격 측정(telemetry) 데이터가 기록되는데, 이는 주어진 요청에서 드래프터가 실제로 작동했는지 확인할 수 있는 유일하고 깔끔한 방법이며, 상위 업스트림 풀 리퀘스트(pull request)가 자체적인 벤치마크를 도출하는 방식이기도 합니다. 설정 자체는 변하지 않았고 그저 테스트를 둘러싼 뼈대(harness)만 바뀌었을 뿐입니다.
또한 아래의 모든 수치는 전체 설정에서 딱 하나의 플래그만 변경한 상태에서 측정한 것입니다. 즉, 각각의 변화량(델타)은 다른 모든 설정은 켜져 있다고 가정할 때 해당 플래그가 기여한 값이지, 진공 상태에서의 효과가 아닙니다. 플래그들은 서로 상호작용을 합니다. 투기적 디코딩(Speculation)은 스레드 수가 얼마나 중요한지를 변경하고, 리패킹(Repacking)은 플래시 어텐션(flash attention)이 읽어야 할 내용을 바꿉니다. 이러한 변화량들을 단순히 더해서 현재 설정과 초보적인 설정 사이의 격차를 설명할 수는 없으며, 저 또한 그런 척하지 않을 것입니다.
전체 현황판
아래는 제가 하나씩 설명하기 전에, 성능에 영향을 미치는 모든 레버(조절 도구)를 한곳에 모아놓은 것입니다. 디코딩 속도, 초당 토큰 수, 반복 측정의 중간값이며, 각 행은 전체 설정에서 정확히 하나만 변경했을 때의 결과입니다. 백분율은 기존에 공개된 설정 대비 수치이므로, 도움이 되는 플래그를 제거하면 수치 하락(loss)으로 나타납니다.
변경된 레버: chat lon...