AI Briefing

JDK Vector API로 추천 시스템 최적화

·2026.03.03 10:36

핵심 내용

넷플릭스는 배칭과 flat buffer, JDK Vector API로 추천 점수 계산의 CPU 사용량을 줄였다.

자세히 보기

Netflix의 Ranker는 홈 화면 개인화 row를 구동하는 대규모 서비스이고, 그중 video serendipity scoring이 노드당 CPU의 약 **7.5%**를 차지했다. 핵심 질문은 “새 타이틀이 지금까지 본 콘텐츠와 얼마나 다른가”였고, 기존 방식은 후보와 시청 이력 사이의 코사인 유사도를 M×N nested loop로 계산해 확장성이 좋지 않았다.

처음에는 후보와 이력 임베딩을 모아 행렬 곱으로 바꾸는 배칭을 도입했다. 요청의 약 **98%**는 단건이었지만 나머지 **2%**의 대형 배치가 전체 처리량의 절반가량을 차지해, 배칭 최적화가 충분히 의미가 있었다.

하지만 첫 구현은 성능을 오히려 약 5% 악화시켰다. 이유는 두 가지였다.

  • 매 요청마다 double[][]를 새로 만들며 GC pressure가 커졌고, 2차원 배열의 비연속 메모리 구조 때문에 캐시 효율도 나빴다.
  • 자바 기반 행렬곱이 단순한 scalar 구현이라 SIMD를 활용하지 못했다.

다음 단계에서는 데이터를 flat double[] buffer로 바꾸고, ThreadLocal<BufferHolder>로 후보용·이력용 버퍼를 재사용했다. 버퍼는 필요할 때만 커지고 줄어들지 않도록 해 요청당 할당을 줄였고, row-major의 연속 메모리 덕분에 접근 패턴도 예측 가능해졌다.

행렬곱 엔진으로는 먼저 BLAS를 검토했지만, 실전 경로에서는 기대만큼 이득이 없었다. netlib-java의 F2J 경로, JNI 전환 비용, column-major와의 배치 차이, 그리고 임베딩 계산과 맞물린 추가 복사와 할당이 발목을 잡았다.

최종 해법은 JDK Vector API였다. 이 기능은 pure Java로 SIMD를 표현할 수 있어 native 의존성이나 JNI 없이 DoubleVector.SPECIES_PREFERRED로 호스트 CPU에 맞는 lane 폭을 선택하고, fma()로 dot product를 누적한다. 벡터 API가 없을 때는 scalar 경로로 떨어지되, 그마저도 루프 언롤링된 최적화 구현을 사용하도록 MatMulFactory로 분기했다.

결과적으로 이 작업은 단순한 알고리즘 변경이 아니라, 배칭 + flat buffer + ThreadLocal 재사용 + SIMD 커널을 함께 맞춘 최적화였다. 같은 serendipity score를 유지하면서도 요청당 CPU 비용을 낮춰 클러스터 풋프린트를 줄이는 방향으로 정리됐다.

이 한국어 요약은 AI가 자동으로 만들었습니다. 원문의 주장과 맥락은 원문에서 확인해 주세요. 저작권은 원저작자에게 있습니다.

AI 처리 방식을 확인하거나, 요약 오류와 출처 표기 문제, 삭제 요청을 문의 · 건의로 알려주세요.