[Loki] TSDBIndex.GetChunkRefs에서 불필요한 라벨 조회 제거
PR 링크: grafana/loki#20970 상태: Merged | 변경: +82 / -56
들어가며
Loki의 index-gateway 프로덕션 프로파일에서 라벨 읽기에 많은 CPU 시간이 소비되고 있었다. GetChunkRefs는 청크 참조만 필요한 상황인데도 시리즈의 라벨을 항상 디코딩하고 있었다. 라벨 디코딩에는 심볼 테이블 룩업과 메모리 할당이 수반되므로, 이를 건너뛸 수 있으면 상당한 성능 개선이 가능하다.
핵심 코드 분석
라벨 건너뛰기 함수 추가
라벨을 디코딩하지 않고 바이너리 데이터에서 라벨 섹션만 건너뛰는 skipSeriesLabels 함수를 추가했다:
func (dec *Decoder) skipSeriesLabels(b []byte) (*encoding.Decbuf, uint64, error) {
d := encoding.DecWrap(tsdb_enc.Decbuf{B: b})
fprint := d.Be64()
k := d.Uvarint()
for range k {
_ = d.Uvarint() // label name offset - 무시
_ = d.Uvarint() // label value offset - 무시
if d.Err() != nil {
return nil, 0, errors.Wrap(d.Err(), "read series label offsets")
}
}
return &d, fprint, nil
}
Series 함수에서 조건부 분기
func (dec *Decoder) Series(version int, b []byte, seriesRef storage.SeriesRef,
from int64, through int64, lbls *labels.Labels, chks *[]ChunkMeta) (fprint uint64, err error) {
var d *encoding.Decbuf
if lbls == nil {
d, fprint, err = dec.skipSeriesLabels(b)
} else {
d, fprint, err = dec.prepSeries(b, lbls, nil)
}
// ...
}
head_read.go에서도 동일한 nil 체크
func (h *headIndexReader) Series(ref storage.SeriesRef, ..., lbls *labels.Labels, ...) (uint64, error) {
// ...
if lbls != nil {
lbls.CopyFrom(s.ls)
}
// ...
}
벤치마크 결과
TSDBIndex_GetChunkRefs-28 allocs/op: 27 → 19 (-29.63%)
TSDBIndex_GetChunkRefs-28 sec/op: 860.3µ → 836.5µ (-2.77%)
왜 이게 좋은가
- 할당 30% 감소:
GetChunkRefs에서 메모리 할당이 27개에서 19개로 줄었다. 실제 프로덕션에서는 시리즈 수가 훨씬 많으므로 효과가 크다. - 심볼 테이블 룩업 제거: 라벨 이름과 값의 심볼 테이블 조회를 완전히 건너뛴다. 이 조회는 해시 맵 룩업을 포함하므로 비용이 적지 않다.
- API 호환성 유지:
lbls를nil로 전달하면 라벨을 건너뛰는 옵트인 방식이므로, 기존 호출부는 영향 받지 않는다. - prepSeries 리팩터링: 기존의
prepSeries와prepSeriesBy를 하나로 통합하면서by필터 로직을 단순화했다.
참고 자료
관련 포스트
PR Analysis 의 다른글
- 이전글 [Loki] TSDB 풀에 전체 슬라이스를 올바르게 반환하여 메모리 할당 99.6% 감소
- 현재글 : [Loki] TSDBIndex.GetChunkRefs에서 불필요한 라벨 조회 제거
- 다음글 [triton] Gluon에서 3D Dot FMA 연산 노출
댓글