[Loki] Thor 쿼리 엔진 메모리 최적화 Part 2: 식별자 캐싱과 빌더 Reserve
PR 링크: grafana/loki#20473 상태: Merged | 변경: +164 / -88
들어가며
Loki의 Thor 쿼리 엔진 메모리 최적화 시리즈의 두 번째 PR입니다. 프로파일링 결과를 기반으로, FQN(Fully Qualified Name) 파싱의 반복 비용과 Arrow 빌더의 점진적 확장 비용을 개선합니다. 정확성 테스트 스위트에서 실행 시간 7%, 메모리 할당 12% 절감 효과를 확인했습니다.
핵심 코드 분석
최적화 1: 식별자 캐시 도입
// Before: 매번 FQN 파싱
for idx, field := range input.Schema().Fields() {
ident, err := semconv.ParseFQN(field.Name)
// ...
}
// After: 캐시에서 조회
type expressionEvaluator struct {
identCache *semconv.IdentifierCache
}
for idx, field := range input.Schema().Fields() {
ident, err := e.identCache.ParseFQN(field.Name)
// ...
}
ParseFQN은 문자열 파싱과 Identifier 구조체 생성을 수반합니다. 같은 FQN이 배치마다 반복적으로 파싱되므로 캐시가 효과적입니다. 캐시는 expressionEvaluator를 포인터 리시버(*expressionEvaluator)로 변경하여 공유합니다.
최적화 2: Arrow 빌더 사전 예약
// Before: 타입별 빌더 개별 Reserve 없음
builder := array.NewBooleanBuilder(memory.DefaultAllocator)
// After: 배치 크기만큼 사전 예약
builder := array.NewBooleanBuilder(memory.DefaultAllocator)
builder.Reserve(int(batch.NumRows()))
모든 데이터 타입(Boolean, String, Uint64, Int64, Float64, Timestamp)의 빌더에 Reserve를 추가했습니다. 또한 aggregator의 개별 필드 Reserve를 단일 rb.Reserve(total)로 통합했습니다.
최적화 3: 파이프라인별 캐시 인스턴스
func newKeepPipeline(...) {
identCache := semconv.NewIdentifierCache()
return newGenericPipelineWithRegion(func(ctx context.Context, ...) {
for i, field := range batch.Schema().Fields() {
ident, err := identCache.ParseFQN(field.Name)
// ...
}
})
}
각 파이프라인 단계(keep, expand, compat)에 독립적인 캐시를 배치하여 동시성 문제 없이 캐시를 활용합니다.
왜 이게 좋은가
1. 정량적 개선 효과
PR 설명에 따르면, 정확성 테스트 스위트에서 실행 시간 7% 감소, 메모리 할당 12% 감소를 달성했습니다. 이는 프로파일링 기반의 타겟팅된 최적화의 전형적인 효과입니다.
2. 값 리시버에서 포인터 리시버로 전환
// Before: 값 복사로 인한 캐시 무효화
func (e expressionEvaluator) eval(...)
// After: 포인터로 캐시 공유
func newExpressionEvaluator() *expressionEvaluator
Go에서 값 리시버는 호출마다 구조체를 복사하므로, 내부 캐시가 매번 초기화됩니다. 포인터 리시버로 전환하면 캐시가 호출 간에 유지됩니다.
3. 점진적 최적화 전략
Part 1, 2, 3으로 나누어 진행하는 접근은 각 변경의 효과를 독립적으로 측정하고 검증할 수 있게 합니다.
참고 자료
- Apache Arrow Go: Builder.Reserve — 빌더 사전 예약 API
- Grafana Loki PR #20530 — 메모리 최적화 Part 3
관련 포스트
PR Analysis 의 다른글
- 이전글 [Triton] Proton 프로파일러에서 불필요한 lock 추가 제거
- 현재글 : [Loki] Thor 쿼리 엔진 메모리 최적화 Part 2: 식별자 캐싱과 빌더 Reserve
- 다음글 [Loki] Partition Ring 셔플 샤딩 캐시 크기를 설정 플래그로 추출
댓글