본문으로 건너뛰기

[Loki] Plain 디코더 벤치마크 추가 및 코드 개선

PR 링크: grafana/loki#20423 상태: Merged | 변경: +101 / -26

들어가며

성능 최적화의 첫 번째 단계는 측정입니다. Loki의 dataobj 모듈에서 bitmap, delta, RLE 디코더에는 벤치마크가 있었지만, plain bytes 디코더에는 없었습니다. 이 PR은 다양한 시나리오와 배치 크기에 대한 체계적인 벤치마크를 추가합니다.

핵심 코드 분석

불필요한 빈 슬라이스 체크 제거 및 에러 처리 개선

Before (value_encoding_plain.go):

func (dec *plainBytesDecoder) Decode(s []Value) (int, error) {
    if len(s) == 0 {
        return 0, nil
    }

    var err error

    for i := range s {
        err = dec.decode(&s[i])
        if errors.Is(err, io.EOF) {
            // ...

After:

func (dec *plainBytesDecoder) Decode(s []Value) (int, error) {
    for i := range s {
        err := dec.decode(&s[i])
        if err != nil && errors.Is(err, io.EOF) {
            // ...

두 가지 개선이 있습니다:

  1. len(s) == 0 체크 제거: 빈 슬라이스에서는 for range가 자연스럽게 0회 실행됩니다
  2. err != nil 선행 체크 추가: errors.Is는 리플렉션을 사용하므로, nil인 경우 불필요한 비용을 방지합니다

체계적인 벤치마크 설계

scenarios := []scenario{
    {
        name: "constant-size",  // 모든 값이 100바이트
        makeValues: func() []Value { /* ... */ },
    },
    {
        name: "variable-size",  // 16~1024바이트 랜덤
        makeValues: func() []Value { /* ... */ },
    },
}
batchSizes := []int{1024, 2048, 4096, 8192}

벤치마크 결과 (Apple M1 Max):

시나리오 Batch 1024 Batch 8192
constant-size 306μs (6.0 GiB/s) 308μs (6.0 GiB/s)
variable-size 116μs (15.9 GiB/s) 120μs (15.5 GiB/s)

왜 이게 좋은가

  • 배치 크기에 따른 성능 변화를 측정하여 최적 배치 크기를 결정할 수 있는 데이터를 제공합니다
  • b.SetBytes()rows/s 커스텀 메트릭으로 처리량을 직관적으로 파악할 수 있습니다
  • b.Loop() 패턴으로 Go 최신 벤치마크 모범 사례를 따릅니다
  • constant-size vs variable-size 시나리오 구분으로 데이터 특성에 따른 성능 차이를 확인할 수 있습니다
  • 배치 크기가 커져도 성능이 거의 일정한 것은 디코더가 메모리 효율적으로 구현되었음을 보여줍니다

참고 자료

댓글

관련 포스트

PR Analysis 의 다른글