FlashVector는 모델 서빙 스택 전체를 자동 최적화하는 LLM 에이전트다.

FlashVector: Agent for Hierarchical Model Serving Stack Optimization

arXiv2609.17391v1

Qi Wu2026-09-15조회 4

무엇인가

프로덕션 추천 시스템에서 모델 서빙은 가장 큰 비용 요인 중 하나다. 처리량을 끌어올리려면 GPU 커널, ML 프레임워크의 계산 그래프, 모델 서버, 온디맨드 피처 처리라는 네 개 층을 동시에 다뤄야 하는데, 각 층은 서로 다른 구현 언어(모델 서버의 Go/C++, 프레임워크 런타임의 Python, 커널의 CUDA/Triton)와 서로 다른 프로파일링 도구(eBPF, PyTorch Kineto, Nsight Systems)를 요구한다. 논문은 이런 교차 계층 전문성이 습득하기 어렵고, 계속 커지고 바뀌는 워크로드를 따라가지 못해 상당한 비용 효율 개선이 실현되지 않은 채 남는다고 지적한다. 최근 LLM 기반 커널 설계 에이전트가 단일 GPU 커널 최적화에서 전문가 수준 효율을 보였지만, 나머지 서빙 스택의 자동 튜닝은 거의 탐구되지 않았다는 것이 이 논문의 출발점이다.

어떻게 동작하나

FlashVector의 핵심 기여는 단일 커널 최적화 에이전트 패러다임을 이기종 기술 스택으로 일반화하는 확장 가능한 프레임워크다. 구조는 지식베이스 로딩, 반복 최적화, 지식 정제의 세 단계다. 지식베이스는 비즈니스 로직(요청당 광고 후보 수, 입력 피처 구조, 게이머 수준 피처와 후보 수준 피처의 차이), 모델 서버(NVIDIA Triton 아키텍처와 배치 크기·큐 길이 같은 런타임 파라미터), 피처 스토어(피처의 데이터 타입·계보·정의), ML 모델(아키텍처 요약, 파라미터 목록, 서빙 설정), 하드웨어(NVIDIA GPU 프로파일링·최적화 절차), 그리고 인간 전문가가 정리한 최적화 쿡북을 담는다. 각 서빙 계층은 동일한 최적화 에이전트 인터페이스에 맞춰 같은 루프에 꽂히는데, Profile(계층별 도구 — GPU 커널은 Nsight, 모델 서버는 eBPF), Diagnose(계층별 지식베이스 대비 병목 진단), Optimize(코드 또는 설정 변경 생성·적용), Verify(프로덕션 증거 대비 결과 확인)에 실행 후 지식베이스에 발견을 되쓰는 Refine 단계가 붙는다.

무엇과 다른가

최적화 루프는 매 반복마다 전체 계층을 훑지 않고 한 계층만 샘플링해 그 계층 전용의 네 단계를 돌린다. Diagnose는 후보 가설을 전체 시간에서 차지하는 비중 곱하기 기대 이득으로 점수화해 순위를 매기고, 최선의 이득이 측정 노이즈보다 작은 후보는 건너뛴다. 검증 기준은 변경 종류에 따라 다르다. 연산 순서와 누적 정밀도를 보존하는 결정적 재작성은 원소별로 정확히 같아야 하고, 부동소수점 재배열은 원소별 절대 편차 10^-3 이내, 10^-3을 넘는 원소가 0.1% 이하(단위 스케일에서 FP16 ULP 하나, FP32 서빙은 10^-4)여야 하며 비유한 출력은 즉시 실패 처리한다. 커널별 검증기는 dtype에 맞춘 한계(FP32 10^-3, FP16 10^-2)를 적용하는데, 변경이 이 한계를 조일 수는 있어도 풀 수는 없다. 의도적 근사는 출력 동일성이 아니라 비즈니스 지표로, 설정 변경은 고정 지연 SLO에서의 섀도 트래픽 카나리로 게이트한다. 정확성만으로는 채택되지 않는다. 개선폭이 노이즈 플로어 ε(수정하지 않은 베이스라인의 반복 no-op 실행으로 추정한 종단 간 ±6%)를 넘어야 하고, 2~6% 구간의 개선은 최대 8배 표본으로 재측정해 플로어를 1/√k로 조인다(8배에서 2.1%). 각 반복이 샘플링한 계층만 검증하므로, 국소 검증을 통과한 후보도 커밋 전에 전체 스택 부하 테스트를 한 번 더 거쳐 종단 간 처리량과 지연이 프로덕션 대표 부하에서 유지되는지 확인하고, 여기서 회귀하면 국소 통과 여부와 무관하게 버린다. Refine 단계는 실행 중 도입된 변경과 지식베이스 수정을 집계해 제자리에서 갱신하는데, 매 단계 전체 최적화 기억을 쌓는 대신 이렇게 하면 컨텍스트 창 크기를 크게 줄이고 치명적 망각과 환각을 실질적으로 완화한다고 저자들은 말한다. 루프는 사람이 호출하지 않는다. 새 릴리스가 플릿에 들어올 때, 그리고 트래픽을 계속 처리하지만 너무 오래 분석되지 않아 설정을 현재 것으로 가정할 수 없을 때 스스로 재트리거된다.

어떻게 쓰나

Unity의 Vector 광고 플랫폼에 배포한 뒤 FlashVector는 모델 서버에서 최대 2배 처리량 증가와 최대 1.98배 지연 단축, 피처 스토어에서 최대 1.6배 처리량 증가를 달성했다. 저자들이 강조하는 점은 이 최적화가 GPU 커널과 계산 그래프 수준뿐 아니라 모델 서버(NVIDIA Triton의 C++ 코드베이스)와 온디맨드 피처 변환 서비스(Python 코드베이스)에서도 나왔다는 것이며, 이것이 프레임워크가 더 복잡한 시스템 아키텍처로 확장된다는 증거라고 주장한다.

전제와 한계

케이스 1은 초기 단계 two-tower 검색 모델이다. 진단 단계에서 병목이 NVIDIA Triton Inference Server의 한 단계, 즉 클라이언트가 gRPC로 보낸 서버 입력을 Triton Python 백엔드(별도 병렬 프로세스)가 기대하는 다른 형식으로 변환하는 지점에 있음을 찾아냈다. 변환 로직이 최적화되지 않은 Python 역직렬화 모듈로 구현돼 있었고, 모델이 문자열 입력을 많이 쓸수록 이 변환이 주요 병목으로 드러났다. 최적화 단계는 이 로직을 C++로 다시 쓰자고 제안했고, 검증에서 30배 속도 향상을 확인했다. 이 수정은 NVIDIA Triton Inference Server 프로젝트에 기여로 되돌려졌다. 케이스 2는 멀티태스크 추천 모델로, PyTorch AOTInductor로 AOT 컴파일되고 FP16 양자화된 베이스라인을 NVIDIA RTX PRO 6000 Blackwell GPU에서 요청당 배치 크기 2000으로 돌렸다. 융합 어텐션 최적화에서는 후보-위치 쌍마다 레이어를 평가하며 수백 MB의 중간 텐서를 GPU 메모리에 쓰고 읽던 오버헤드를, 상호작용 전에 후보 전용·위치 전용 피처 블록을 미리 계산하는 방식으로 제거했다. 이 구조 덕분에 6개의 개별 GPU 커널을 중간 작업값을 모두 GPU 레지스터에 유지하는 단일 커널로 합칠 수 있었고, 원래 연산 순서와 누적 정밀도를 보존해 어텐션 가중치가 베이스라인과 비트 단위로 동일함을 원소별 비교로 검증했다. 모듈당 메모리 트래픽이 약 3분의 1로 줄고 어텐션 지연이 996.5µs에서 279.8µs로(3.56배) 단축됐다. 임베딩 계층에서는 embedding bag 커널이 GPU 시간의 28.1%를 소비한다는 것을 진단했는데, PyTorch가 이 연산자를 불투명한 ATen 디스패처 커널로 매핑해 AOTInductor가 인접 리덕션과 융합하지 못하고 있었다. FlashVector는 마스크 평균 풀링을 F.embedding, 마스킹, 합산이라는 세밀한 PyTorch 프리미티브로 분해해 전체 룩업·리덕션 로직을 컴파일러에 노출시켜 단일 Triton 커널로 융합되게 했다. 또한 타깃·시퀀스 의미 임베딩을 포워드 패스 시작에서 한 번만 평가해 dense와 게이팅 분기에서 재사용하도록 중복 계산을 제거했다. 이 변경들을 합쳐 총 GPU 커널 시간 33.4% 감소, 포워드 패스 지연 30.1% 감소, 디스패처 오버헤드 44.8%에서 28.5%로 감소를 얻었고, 주 병목이 임베딩 룩업에서 GEMM 커널로 이동했다.

케이스 3은 CPU 전용 온디맨드 피처 전처리 서비스다. 요청마다 컨텍스트 행 하나와 최대 2000개의 후보 행을 처리하고, 단일 파드에서 12개의 Python 백엔드 인스턴스가 14개 vCPU에 걸쳐 병렬로 요청을 처리한다. 진단은 비용을 입력 수신, 피처 계산, 출력 텐서 조립으로 나눴다. 입력 수신에서는 프로덕션 요청이 875개의 입력을 나르는데 대부분 숫자형이고 단지 텐서로 재조립되기 위해 읽힌다는 점에 주목해, 숫자 텐서를 서버 버퍼에 대한 읽기 전용 뷰로 노출하고 문자열 텐서는 원소마다 객체를 만들고 디코드 루프를 도는 대신 C++에서 한 번 훑는 네이티브 뷰를 도입했으며, 입력의 유일한 소비자가 어휘 룩업인 경우 C++가 공유 메모리 어휘를 직접 조회해 텍스트가 Python에 도달하지 않게 하는 네이티브 매핑을 적용했다. 피처 계산에서는 벡터화된 NumPy/pandas 연산에 너무 불규칙한 부분(ragged 루프, 행별 딕셔너리, 문자열 처리)을 손으로 작성한 Cython 커널로 대체했고, 텐서 조립에서는 피처마다 작은 배열을 할당한 뒤 연결하던 두 패스(서빙 CPU의 약 20%)를, 각 목적지를 한 번만 할당하고 모든 피처가 자기 슬라이스에 직접 쓰게 바꿔 연결과 중복 복사를 모두 제거했다. 세 최적화는 서로 다른 반복에서 구현·평가됐고 각각 두 자릿수 이득을 내며 누적됐다. 고정 지연 SLO에서 전처리 파드 하나가 유지하는 처리량은 베이스라인 940 req/s에서 세 가지가 모두 적용된 뒤 약 1.5k req/s로 1.6배가 됐다. 케이스 4는 코드가 아니라 런타임 설정을 다룬다. 동적 배칭 설정, 요청 큐 정책, GPU에 상주하는 모델 인스턴스 수 등을 주변 스택이 바뀔 때마다, 자기 코드 최적화가 반영된 뒤에도 다시 튜닝한다. 이미 코드 최적화된 단일 모델에서 측정한 설정들 사이에도 같은 지연 SLO에서 지속 처리량이 2배 이상 차이 났다. 현재 스윕은 인스턴스 수와 동적 배치 크기 두 개를 바꾸고 큐 정책은 탐색 대신 SLO에서 유도하며, 손으로 튜닝한 베이스라인은 배치 크기를 프레임워크 기본값으로 두고 활용률 목표로 스케일하고 있었다. 이 계층의 Profile은 각 후보 설정을 고정 부하에서 돌려 고정 지연 SLO에서의 지속 처리량을 기록하고, Diagnose는 시험들을 순위 매겨 최선을 고르거나 측정 노이즈 이상으로 기존을 앞서는 것이 없으면 거절한다. Optimize는 그 설정을 풀 리퀘스트로 열어 적용하고, Verify는 섀도 트래픽 카나리로 프로덕션에서 시험해 유지하거나 버리며 어느 결과든 지식베이스에 기록한다. 합성 요청이 아니라 실제 프로덕션 요청을 재생해 충실도를 확보했고, 2단계 모델의 경우 짝을 이루는 전처리기 출력을 먼저 캡처해 모델 입력으로 재생했다. 이렇게 튜닝한 프로덕션 모델 네 개가 표에 제시된다.

한국 개발자 입장에서 이 논문의 실무적 의미는 세 가지다. 첫째, 서빙 비용 최적화를 커널 하나의 문제로 보지 말라는 것이다. 저자들은 비커널 계층에 커널만큼, 어쩌면 그 이상의 최적화 기회가 있다고 말하며, 실제로 가장 극적인 사례 중 하나는 Triton 서버의 Python 역직렬화를 C++로 바꾼 30배 향상이었다. 둘째, 최적화를 코드 변경과 파라미터 튜닝의 공동 문제로 다뤄야 한다. 코드를 최적화한 뒤에도 설정을 재튜닝하지 않으면 같은 SLO에서 처리량이 2배 이상 갈렸다. 셋째, 자동 최적화를 도입할 때 검증 설계가 성능만큼 중요하다. 이 시스템은 결정적 재작성, 부동소수점 재배열, 의도적 근사, 설정 변경에 서로 다른 정확성 기준을 적용하고, 노이즈 플로어와 전체 스택 부하 테스트를 통과한 변경만 채택한다. 사내에서 유사한 에이전트를 만들 계획이라면 프로파일 도구의 계층별 이질성, 지식베이스의 제자리 갱신, 그리고 사람이 호출하지 않는 자동 재트리거를 설계 요구사항으로 잡아야 한다.

논문은 별도의 한계 절을 두지 않지만 본문에 전제와 제약이 드러난다. 검증 임계값(10^-3, 10^-4, 10^-2, 0.1%)과 노이즈 플로어(±6%, 8배 재측정 시 2.1%)는 이 배포 환경에서 추정된 값이며, 각 반복이 한 계층만 프로파일·검증하므로 국소 검증을 통과한 변경도 전체 스택 부하 테스트에서 회귀하면 버려진다. 즉 계층별 이득이 종단 간 이득으로 이어진다는 보장은 없고, 매 후보마다 전체 스택 재측정 비용을 치러야 한다. 지식베이스는 비즈니스 로직, 모델 서버, 피처 스토어, 모델, 하드웨어, 쿡북 항목을 사람이 큐레이션해 유지해야 하며, 저자들도 이 지식베이스의 제자리 갱신이 컨텍스트 창과 환각 억제에 결정적이라고 말한다. 또한 케이스 4의 설정 스윕은 현재 인스턴스 수와 동적 배치 크기 두 축만 탐색하고 큐 정책은 SLO에서 유도하는 등 탐색 공간이 아직 좁다. 평가는 Unity Vector라는 단일 프로덕션 플랫폼의 NVIDIA GPU 환경에서 이뤄졌고, 표 1의 요약 수치와 표 2의 커널별 개별 속도 향상 값, 표 4의 모델별 결과는 본문에 구체적 숫자로 제시되지 않았다.