[Grafana Loki] 델타 디코더 벤치마크 개선 및 Decode 메서드 성능 측정 추가
PR 링크: grafana/loki#20419 상태: Merged | 변경: +64 / -52
들어가며
Loki의 데이터 오브젝트는 타임스탬프 같은 단조 증가 정수 시퀀스를 델타 인코딩으로 저장한다. 기존 벤치마크는 내부 decode() 메서드를 한 번에 하나씩 호출하는 방식이었는데, 실제 사용 패턴은 Decode([]Value) 메서드로 배치 단위로 디코딩하는 것이다. 이 PR은 벤치마크를 실제 사용 패턴에 맞게 재작성하고, 처리량 메트릭(rows/s, MB/s)을 추가했다.
핵심 코드 분석
Before: 단일 값 decode 벤치마크
func Benchmark_deltaDecoder_Decode(b *testing.B) {
b.Run("Sequential", func(b *testing.B) {
enc := newDeltaEncoder(&buf)
dec := newDeltaDecoder(&buf)
for i := 0; i < b.N; i++ {
_ = enc.Encode(Int64Value(int64(i)))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = dec.decode() // 내부 메서드, 1개씩
}
})
}
After: 배치 Decode + 처리량 메트릭
func Benchmark_deltaDecoder_Decode(b *testing.B) {
pageSize := 1 << 16 // 65536
scenarios := map[string]func() *bytes.Buffer{
"sequential": func() *bytes.Buffer { /* ... */ },
"largest delta": func() *bytes.Buffer { /* ... */ },
"random": func() *bytes.Buffer { /* ... */ },
}
batchSizes := []int{256, 1024, 4096}
for datasetName, makeDataset := range scenarios {
for _, batchSize := range batchSizes {
b.Run(fmt.Sprintf("%s/batchSize=%d", datasetName, batchSize), func(b *testing.B) {
buf := makeDataset()
decBuf := make([]Value, batchSize)
reader := bytes.NewReader(buf.Bytes())
dec := newDeltaDecoder(reader)
valuesRead := 0
for b.Loop() {
reader.Reset(buf.Bytes())
dec.Reset(reader)
for {
n, err := dec.Decode(decBuf)
valuesRead += n
if err != nil && errors.Is(err, io.EOF) {
break
}
}
}
b.SetBytes(int64(pageSize * int(unsafe.Sizeof(int64(0)))))
b.ReportMetric(float64(valuesRead)/float64(b.Elapsed().Seconds()), "rows/s")
})
}
}
}
errors.Is 최적화
// Before
if errors.Is(err, io.EOF) {
// After
if err != nil && errors.Is(err, io.EOF) {
왜 이게 좋은가
- 실제 사용 패턴 반영:
Decode([]Value)배치 메서드를 벤치마크하여, 실제 쿼리 실행 시의 성능을 정확하게 측정한다. 배치 크기(256, 1024, 4096)별 성능 차이도 확인할 수 있다. - 처리량 메트릭:
b.SetBytes와b.ReportMetric으로 MB/s와 rows/s를 동시에 보고한다. 결과: 순차 데이터 1130MB/s(148M rows/s), 랜덤 데이터 370MB/s(48M rows/s)로, 데이터 패턴에 따른 성능 차이가 명확히 드러난다. - errors.Is 빠른 경로:
err != nil체크를 먼저 수행하여, 에러가 없는 일반 경로에서errors.Is의 리플렉션 비용을 회피한다. 디코딩 루프에서는 대부분의 반복이 에러 없이 진행되므로 유의미한 최적화다.
벤치마크가 실제 사용 패턴을 반영하지 않으면 최적화 방향을 잘못 잡을 수 있다. 이 PR은 벤치마크 자체의 품질을 향상시켜 향후 인코딩 최적화의 기반을 마련했다.
참고 자료
관련 포스트
PR Analysis 의 다른글
- 이전글 [triton] AMD: padded shared layout을 더 작은 block size에도 적용하여 bank conflict 제거
- 현재글 : [Grafana Loki] 델타 디코더 벤치마크 개선 및 Decode 메서드 성능 측정 추가
- 다음글 [Grafana Loki] pkg/dataobj를 위한 실험적 arena 스타일 메모리 패키지 도입
댓글