[loki] Grafana Loki LogQL 성능 최적화: Constant-Label Fast Path 도입
PR 링크: grafana/loki#24368 상태: Merged | 변경: +829 / -75
들어가며
Grafana Loki의 LogQL 쿼리 엔진은 로그 라인을 처리할 때마다 매번 레이블을 빌드하고 파이프라인을 실행하는 구조를 가지고 있습니다. 하지만 sum(count_over_time(...))과 같이 스트림의 레이블이 쿼리 결과에서 변하지 않는 경우에도, 기존 엔진은 매 라인마다 불필요하게 LabelsBuilder를 초기화하고 메타데이터를 처리하는 비용을 지불하고 있었습니다. 본 PR은 이러한 중복 작업을 제거하는 'Fast Path'를 도입하여 성능을 획기적으로 개선합니다.
코드 분석
1. Stage 인터페이스에 Hints() 추가
각 파이프라인 단계가 레이블을 변경할 수 있는지 여부를 판단하기 위해 Stage 인터페이스에 Hints() 메서드를 추가했습니다.
Before:
// 기존에는 각 필터가 독립적으로 로직을 수행
func (e existsFilter) ToStage() Stage {
return StageFunc{
process: func(_ int64, line []byte, _ *LabelsBuilder) ([]byte, bool) {
return line, e.Filter(line)
},
}
}
After:
// StageHints를 통해 레이블 변경 가능 여부를 명시
func (e existsFilter) ToStage() Stage {
return NewStageFunc(nil, StageHints{CanModifyLabels: false}, func(_ int64, line []byte, _ *LabelsBuilder) ([]byte, bool) {
return line, e.Filter(line)
})
}
이를 통해 쿼리 엔진은 파이프라인 전체가 레이블을 변경하는지 여부를 정적으로 분석할 수 있게 되었습니다.
2. BaseLabelsBuilder의 정확성 개선
최적화 과정에서 기존 캐싱 로직의 잠재적 버그를 수정했습니다. 기존에는 64비트 해시값만으로 캐시를 구분하여 해시 충돌 시 레이블이 섞이는 문제가 있었습니다.
Before:
// 해시값만으로 캐싱하여 충돌 가능성 존재
cache[hash] = labelsResult
After:
// 추출기 생성 시점에 레이블을 계산 및 캐싱하여 안전성 확보
// 이 수정은 'Boy Scout Rule'에 따라 최적화의 안정성을 위해 선행됨
왜 이게 좋은가
성능 수치
벤치마크 결과, 레이블이 고정된 쿼리 패턴에서 sec/op가 약 72%에서 93%까지 감소했습니다. 특히 group_by_labels_but_no_stage_processing 케이스에서는 할당(allocs/op)이 1에서 0으로 줄어들며 메모리 효율성이 극대화되었습니다.
핵심 교훈
- 정적 분석의 활용: 파이프라인의 각 단계에
Hints()를 도입함으로써, 런타임에 매번 계산할 필요 없이 컴파일 타임(또는 쿼리 초기화 시점)에 최적화 경로를 결정할 수 있습니다. - Boy Scout Rule: 성능 최적화를 위해 코드를 수정할 때, 기존에 존재하던 잠재적 버그(해시 충돌)를 함께 수정함으로써 시스템의 전체적인 신뢰도를 높였습니다.
- 불필요한 연산 제거:
streamLineSampleExtractor가 단순히Process를 건너뛰는 것을 넘어, 레이블 계산 자체를 생략하는 구조로 변경함으로써 CPU 사이클을 크게 절약했습니다.
리뷰어 논의
리뷰 과정에서 joe-elliott은 해시 충돌 가능성과 map[string] 사용 여부에 대해 질문했습니다. pracucci는 레이블을 문자열로 변환하는 비용이 더 크기 때문에 현재의 해시 기반 방식이 효율적임을 설명했습니다. 또한, 기존 streamLineSampleExtractor의 'noop' 경로와 이번 최적화의 차이점을 명확히 하여, 단순 프로세스 스킵과 레이블 재계산 생략의 차이를 분명히 했습니다.
참고 자료
- https://github.com/grafana/loki/blob/main/pkg/logql/log/pipeline.go
- https://github.com/grafana/loki/blob/main/pkg/logql/log/metrics_extraction.go
⚠️ 알림: 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.
관련 포스트
PR Analysis 의 다른글
- 이전글 [flashinfer] Blackwell 아키텍처에서 FlashInfer Ragged Prefill 성능 3.3배 향상시키기
- 현재글 : [loki] Grafana Loki LogQL 성능 최적화: Constant-Label Fast Path 도입
- 다음글 [onnxruntime] ONNX Runtime, SM90 GPU를 위한 네이티브 FP8 행렬 곱셈 최적화 도입
댓글