memory-optimization

2 개의 포스트

TurboQuant: 극한의 압축으로 AI 효율성을 재정의하다 (새 탭에서 열림)

Google Research가 발표한 **TurboQuant**는 대규모 언어 모델(LLM)과 벡터 검색 엔진의 효율성을 극대화하기 위해 설계된 이론 기반의 압축 알고리즘입니다. 이 기술은 기존 양자화 방식의 고질적인 문제였던 메모리 오버헤드를 완전히 해결하여, 모델 성능 저하 없이 KV(Key-Value) 캐시 크기를 6배 이상 줄이고 추론 속도를 최대 8배까지 향상시킵니다. 결과적으로 TurboQuant는 추가적인 파인튜닝 없이도 초거대 AI 모델의 메모리 병목 현상을 해결하는 실질적인 솔루션을 제시합니다. ### 기존 양자화 방식의 한계와 메모리 오버헤드 * 전통적인 벡터 양자화는 데이터 크기를 줄이는 데 효과적이지만, 각 데이터 블록마다 정밀한 양자화 상수를 별도로 계산하고 저장해야 하는 '메모리 오버헤드'가 발생합니다. * 이러한 상수는 숫자당 보통 1~2비트의 추가 용량을 차지하며, 이는 전체 압축 효율을 떨어뜨리는 주요 원인이 됩니다. * 고차원 벡터를 사용하는 AI 모델에서는 이러한 오버헤드가 누적되어 KV 캐시의 병목 현상을 심화시키고 전체 시스템의 메모리 비용을 증가시킵니다. ### PolarQuant: 극좌표계를 활용한 혁신적 압축 * PolarQuant는 벡터를 기존의 데카르트 좌표계(X, Y, Z) 대신 극좌표계(반지름과 각도)로 변환하여 처리하는 새로운 접근 방식을 취합니다. * 데이터의 각도가 특정 패턴으로 집중되어 있다는 점을 활용하여, 경계값이 계속 변하는 사각형 그리드 대신 고정된 원형 그리드에 데이터를 매핑합니다. * 이를 통해 매번 정규화 단계를 거칠 필요가 없어져 기존 양자화 방식이 가졌던 메모리 오버헤드를 근본적으로 제거합니다. * 반지름 쌍을 재귀적으로 변환하여 최종적으로는 단 하나의 반지름과 데이터의 의미를 담은 여러 각도로 데이터를 압축합니다. ### QJL: 1비트의 마법을 통한 오차 제거 * QJL(Quantized Johnson-Lindenstrauss) 알고리즘은 데이터의 필수적인 거리와 관계를 유지하면서 고차원 데이터를 1비트 부호(+1 또는 -1)로 압축합니다. * TurboQuant의 두 번째 단계에서 사용되며, 첫 번째 단계(PolarQuant)에서 발생한 미세한 잔차 오차를 제거하는 수학적 오류 체크 역할을 수행합니다. * 고정밀 쿼리와 저정밀 데이터를 전략적으로 결합하는 특수 추정기(Estimator)를 사용하여 모델이 어텐션 스코어를 계산할 때 편향 없는 정확한 결과를 도출하게 돕습니다. ### 실험 결과 및 성능 지표 * **성능 유지:** LongBench, RULER 등 다양한 벤치마크에서 Gemma와 Mistral 모델을 테스트한 결과, KV 캐시를 3비트로 양자화해도 성능 저하가 거의 없는 것으로 나타났습니다. * **압축 효율:** 추가적인 학습이나 파인튜닝 없이도 KV 캐시 메모리 사용량을 최소 6배 이상 절감합니다. * **속도 향상:** H100 GPU 환경에서 4비트 TurboQuant를 적용할 경우, 양자화되지 않은 32비트 키 값을 사용할 때보다 어텐션 로짓 계산 속도가 최대 8배 빨라집니다. TurboQuant는 긴 컨텍스트(Long-context) 처리가 필요한 현대 AI 서비스에서 비용과 성능이라는 두 마리 토끼를 잡을 수 있는 강력한 도구입니다. 특히 하드웨어 자원이 제한된 환경에서 대규모 모델을 운영하거나, 실시간 응답 속도가 중요한 검색 서비스에 도입했을 때 가장 큰 효과를 기대할 수 있습니다.

How Go 1.24's Swiss Tables saved us hundreds of gigabytes (새 탭에서 열림)

Go 1.24에서 도입된 새로운 맵(map) 구현체인 '스위스 테이블(Swiss Tables)'은 대규모 인메모리 데이터를 다루는 서비스에서 획기적인 메모리 절감 효과를 제공합니다. Datadog의 실제 서비스 적용 사례에 따르면, 특정 고부하 환경에서 라이브 힙(Live Heap) 사용량이 500 MiB 감소했으며, 가비지 컬렉터(GOGC)의 영향을 고려할 때 전체 물리 메모리(RSS)는 약 1 GiB까지 절약되었습니다. 이는 Go 1.24의 다른 런타임 오버헤드를 상쇄하고도 남는 수준의 성능 향상을 보여줍니다. **실서비스에서의 메모리 절감 수치** * `ShardRouter` 패키지 내의 `shardRoutingCache`라는 대형 맵에서 약 500 MiB의 라이브 힙 사용량이 감소했습니다. * Go의 기본 GOGC 설정(100)을 기준으로 계산하면, 힙 사용량 감소는 실제 물리 메모리(RSS)에서 약 1 GiB(500 MiB x 2)의 절감으로 이어집니다. * Go 1.24의 다른 회귀 문제(mallocgc 이슈)로 인해 예상되는 400 MiB의 RSS 증가를 고려하더라도, 결과적으로 600 MiB의 순 메모리 감소가 확인되었습니다. **데이터 구조와 메모리 추정** * 해당 맵은 `string`을 키로, `Response` 구조체를 값으로 가집니다. * `Response` 구조체는 `ShardID`(int32), `ShardType`(int), `RoutingKey`(string header), `LastModified`(*time.Time)로 구성됩니다. * 64비트 아키텍처 기준으로 키-값 쌍 하나당 패딩을 포함해 약 56바이트를 차지하며, 서비스 시작 시 대량으로 생성된 후 런타임 중에는 거의 변경되지 않는 특성을 보입니다. **Go 1.23의 버킷 기반 맵 방식과 한계** * 기존 Go 1.23은 8개의 슬롯을 가진 '버킷' 배열로 해시 테이블을 관리했으며, 버킷 수는 항상 2의 거듭제곱으로 유지되었습니다. * 데이터 삽입 시 버킷 내부의 모든 요소를 순차적으로 스캔해야 하므로 CPU 오버헤드가 발생하며, 버킷이 가득 차면 '오버플로우 버킷'을 체이닝 방식으로 추가했습니다. * 평균 로드 팩터(Load Factor)가 13/16(약 81%)을 초과하면 버킷 배열의 크기를 2배로 늘리는 재할당이 발생하는데, 이 과정에서 점진적 복사(Evacuation) 방식을 사용하여 지연 시간을 관리했습니다. **결론 및 권장사항** 대규모 맵 데이터를 메모리에 유지하는 Go 애플리케이션은 Go 1.24로의 업그레이드만으로도 상당한 메모리 효율성 개선을 기대할 수 있습니다. 특히 읽기 중심의 거대 캐시 시스템이나 데이터 라우팅 테이블을 운영하는 경우, 스위스 테이블 기반의 최적화된 메모리 레이아웃이 비용 절감과 성능 향상에 큰 기여를 할 것입니다.