[Loki] 데이터 오브젝트 Plain Value 디코더 최적화로 처리량 93% 향상
PR 링크: grafana/loki#20441 상태: Merged | 변경: +422 / -265
들어가며
Grafana Loki의 데이터 오브젝트 레이어에서 Plain Value 디코더는 로그 데이터를 읽어 메모리에 로드하는 핵심 컴포넌트입니다. 이 PR은 디코더를 근본적으로 재설계하여, Arrow 스타일 메모리 표현, []byte 기반 디코딩, 포인터 간접 참조 최소화, 인터페이스 대신 구체적 타입 사용 등 여러 최적화를 적용합니다. 벤치마크 결과, geomean 기준 처리량이 약 93% 향상되었습니다.
핵심 코드 분석
페이지 읽기 방식 변경: io.Reader에서 []byte 직접 접근으로
Before:
func (p *MemPage) reader(compression datasetmd.CompressionType) (
presence io.Reader, values io.ReadCloser, err error) {
// io.Reader를 통한 스트림 읽기
bitmapReader := bytes.NewReader(bitmapData)
compressedValuesReader := bytes.NewReader(compressedValuesData)
// ...
return bitmapReader, io.NopCloser(compressedValuesReader), nil
}
After:
type openedPage struct {
PresenceData []byte
ValueData []byte
}
func (p *MemPage) open(compression datasetmd.CompressionType) (
openedPage, io.Closer, error) {
// 직접 바이트 슬라이스 반환
return openedPage{
PresenceData: presenceData,
ValueData: compressedValuesData, // 또는 압축 해제된 데이터
}, closer, nil
}
bufio.Reader 제거
Before:
type pageReader struct {
presenceReader *bufio.Reader
valuesReader *bufio.Reader
valuesDec valueDecoder // 인터페이스
}
After:
type pageReader struct {
presenceDec *bitmapDecoder
valuesDec legacyValueDecoder // 구체적 타입
alloc memory.Allocator // 메모리 할당자 추가
}
bufio.Reader를 제거하고 바이트 슬라이스에 직접 접근함으로써, 버퍼 복사와 인터페이스 호출 오버헤드를 제거했습니다. 또한 memory.Allocator를 도입하여 매 read 호출마다 이전 할당을 회수하는 방식으로 메모리 관리를 개선했습니다.
벤치마크 결과
| 지표 | Before | After | 개선 |
|---|---|---|---|
| constant-size (sec/op) | 257.9us | 113.1us | -56.17% |
| variable-size (sec/op) | 102.07us | 62.74us | -38.54% |
| constant-size (rows/s) | 77.54M | 176.90M | +128.15% |
| variable-size (rows/s) | 38.13M | 62.04M | +62.71% |
| constant-size (allocs/op) | 5 | 1 | -80.00% |
| geomean (B/s) | 11.48Gi | 22.12Gi | +92.67% |
왜 이게 좋은가
- 근본적인 데이터 접근 패턴 변경:
io.Reader인터페이스 기반 스트림 읽기에서[]byte직접 접근으로 전환하여, 함수 호출과 버퍼 복사 오버헤드를 제거했습니다. - 메모리 할당 80% 감소:
constant-size케이스에서 5회 할당이 1회로 줄었습니다. GC 부하가 줄어 실환경에서 더 안정적인 레이턴시를 기대할 수 있습니다. - Arrow 스타일 메모리 표현: 문자열 배열을 offset + data 형식으로 표현하여, 포인터 간접 참조와 개별 문자열 할당을 제거했습니다.
- 점진적 마이그레이션: 기존 구현과 새 구현을 공존시키는 레거시 어댑터를 두어, 엔드-투-엔드 성능 향상을 후속 PR에서 완성할 수 있도록 설계했습니다.
참고 자료
관련 포스트
PR Analysis 의 다른글
- 이전글 [Triton] AMD fine-grained cluster barrier 추가 및 Gluon 노출
- 현재글 : [Loki] 데이터 오브젝트 Plain Value 디코더 최적화로 처리량 93% 향상
- 다음글 [triton] moveUpTranspose 최적화 제거 PR의 Revert - 회귀 방지
댓글