[Grafana Loki] pkg/dataobj를 위한 실험적 arena 스타일 메모리 패키지 도입
PR 링크: grafana/loki#20422 상태: Merged | 변경: +1043 / -0
들어가며
Grafana Loki의 pkg/dataobj에서 대량의 메모리 할당과 해제가 반복되면 GC 압력이 증가합니다. 이 PR은 arena 스타일의 메모리 Allocator를 새로 도입하여, 할당된 메모리 영역을 GC 없이 회수(Reclaim)하고 재사용할 수 있게 합니다. 64바이트 캐시라인 정렬, 비트맵 기반 free-list 관리, LSB 순서 비트맵이 포함됩니다.
핵심 코드 분석
Allocator: arena 스타일 메모리 관리
type Allocator struct {
regions []*Memory
free bitmap // 1=사용 가능, 0=사용 중
empty bitmap // 1=nil(trimmed), 0=not-nil
}
func (alloc *Allocator) Allocate(size int) *Memory {
// 1. free-list에서 적합한 영역 탐색
for i := range alloc.free.IterValues(len(alloc.regions), true) {
region := alloc.regions[i]
if region != nil && cap(region.data) >= size {
alloc.free.Set(i, false)
return region
}
}
// 2. 없으면 64바이트 정렬된 새 영역 생성
region := &Memory{data: allocBytes(size)}
alloc.addRegion(region, false)
return region
}
64바이트 캐시라인 정렬 할당
func allocBytes(size int) []byte {
const alignmentPadding = 64
buf := make([]byte, size+alignmentPadding)
addr := uint64(uintptr(unsafe.Pointer(&buf[0])))
alignedAddr := memalign.Align64(addr)
if alignedAddr != addr {
offset := int(alignedAddr - addr)
return buf[offset : offset+size : offset+size]
}
return buf[:size:size]
}
비트맵: LSB 순서의 비트팩 부울 시퀀스
type bitmap []uint64
func (set bitmap) IterValues(maxIndex int, value bool) iter.Seq[int] {
return func(yield func(int) bool) {
for _, word := range set {
rem := word
if !value {
rem = ^rem // 비트 반전으로 unset 비트 탐색
}
for rem != 0 {
firstSet := bits.TrailingZeros64(rem)
// ...
}
}
}
}
왜 이게 좋은가
- GC 압력 감소: Reclaim으로 영역을 "사용 가능"으로 표시하면 Go GC가 수집할 필요 없이 재사용됩니다. Trim은 미사용 영역만 GC에 반환합니다.
- 캐시라인 정렬: 64바이트 정렬로 CPU 캐시 효율을 높입니다.
- Apache Arrow 호환: LSB 비트 순서는 Apache Arrow의 validity 버퍼와 boolean 배열 표현과 호환됩니다.
- 계층적 Allocator: 부모 Allocator에서 자식을 생성하여 메모리 풀을 계층적으로 관리할 수 있습니다.
- 포괄적 테스트: Allocator의 Reclaim/Trim 동작과 비트맵 연산에 대한 테스트가 포함됩니다.
참고 자료
관련 포스트
PR Analysis 의 다른글
- 이전글 [Grafana Loki] 델타 디코더 벤치마크 개선 및 Decode 메서드 성능 측정 추가
- 현재글 : [Grafana Loki] pkg/dataobj를 위한 실험적 arena 스타일 메모리 패키지 도입
- 다음글 [Loki] Plain 디코더 벤치마크 추가 및 코드 개선
댓글