본문으로 건너뛰기

[starlette] Starlette의 CI 벤치마크 안정화: glibc mmap_threshold 최적화를 통한 업로드 버퍼 할당 개선

PR 링크: Kludex/starlette#3524 상태: Merged | 변경: +16 / -1

들어가며

Starlette은 Python 웹 프레임워크로, 비동기 HTTP 통신에 최적화되어 있습니다. 개발 과정에서 코드의 성능을 지속적으로 측정하고 개선하는 것은 매우 중요합니다. 특히, CI(Continuous Integration) 환경에서의 벤치마크는 코드 변경이 성능에 미치는 영향을 조기에 파악하는 데 핵심적인 역할을 합니다.

이번 PR(Pull Request)은 Starlette의 CI 환경에서 실행되는 벤치마크의 안정성을 높이기 위한 미묘하지만 중요한 최적화를 다룹니다. 구체적으로, glibc의 메모리 할당 동작 방식을 조정하여 파일 업로드 시 발생하는 버퍼 할당의 예측 불가능성을 줄이는 데 초점을 맞추고 있습니다. 이 변경은 특히 multipart 관련 벤치마크에서 발생하는 불안정한 결과를 해결하고, 보다 일관된 성능 측정 기반을 마련하는 것을 목표로 합니다.

코드 변경 분석

이번 PR의 핵심 변경 사항은 .github/workflows/benchmark.yml 파일과 benchmarks/README.md 파일에 집중되어 있습니다. 변경 사항은 주로 glibc의 메모리 할당 동작을 제어하는 환경 변수 GLIBC_TUNABLES를 수정하는 데 있습니다.

1. .github/workflows/benchmark.yml 파일 변경

이 파일은 Starlette의 CI 워크플로우를 정의하며, 벤치마크 실행 환경 설정도 포함합니다.

Before:

 env:
   PYTHONHASHSEED: "0"
   # https://sourceware.org/glibc/manual/latest/html_node/Hardware-Capability-Tunables.html
-      GLIBC_TUNABLES: "glibc.cpu.hwcaps=-AVX512F,-AVX512VL,-AVX512BW,-ERMS,-FSRM"
+      # Keep upload buffers above a fixed mmap threshold to reduce heap-dependent realloc copies.
+      GLIBC_TUNABLES: "glibc.cpu.hwcaps=-AVX512F,-AVX512VL,-AVX512BW,-ERMS,-FSRM:glibc.malloc.mmap_threshold=32768"

After:

 env:
   PYTHONHASHSEED: "0"
   # https://sourceware.org/glibc/manual/latest/html_node/Hardware-Capability-Tunables.html
-      GLIBC_TUNABLES: "glibc.cpu.hwcaps=-AVX512F,-AVX512VL,-AVX512BW,-ERMS,-FSRM"
+      # Keep upload buffers above a fixed mmap threshold to reduce heap-dependent realloc copies.
+      GLIBC_TUNABLES: "glibc.cpu.hwcaps=-AVX512F,-AVX512VL,-AVX512BW,-ERMS,-FSRM:glibc.malloc.mmap_threshold=32768"

설명:

기존에는 GLIBC_TUNABLES 환경 변수를 통해 glibc의 CPU 하드웨어 기능 관련 튜닝 옵션만 설정하고 있었습니다. 하지만 이번 변경으로 glibc.malloc.mmap_threshold=32768 옵션이 추가되었습니다.

  • glibc.malloc.mmap_threshold: 이 설정은 glibc의 메모리 할당자(malloc)가 작은 할당 요청에 대해 sbrk 시스템 콜 대신 mmap 시스템 콜을 사용할지 여부를 결정하는 임계값입니다. 기본적으로 glibc는 이 임계값을 동적으로 조정하여 성능을 최적화하려고 시도합니다. 이 임계값은 일반적으로 64KB (65536 바이트) 근처로 설정되어 있습니다.
  • 32768: 이 값은 32KB를 의미합니다. 이 설정을 통해 mmap 시스템 콜을 사용하는 최소 할당 크기를 32KB로 고정합니다.

주석 # Keep upload buffers above a fixed mmap threshold to reduce heap-dependent realloc copies.는 이 변경의 목적을 명확히 설명합니다. 즉, 업로드 버퍼가 mmap 시스템 콜의 임계값보다 작게 유지되도록 하여, 힙(heap) 영역에 의존적인 realloc 호출 시 발생하는 불필요한 데이터 복사를 줄이겠다는 것입니다.

2. benchmarks/README.md 파일 변경

이 파일은 벤치마크 실행 방법에 대한 문서를 포함합니다.

Before: (관련 내용 없음)

After:

+On Linux with glibc, you can use the same allocator setting as CI:
+
+```console
+GLIBC_TUNABLES=glibc.malloc.mmap_threshold=32768 PYTHONHASHSEED=0 uv run pytest benchmarks/multipart_benchmark.py --codspeed
+```
+
+glibc normally adjusts the
+[`mmap` threshold](https://sourceware.org/glibc/manual/2.39/html_node/Memory-Allocation-Tunables.html)
+based on earlier allocations. This can make an upload buffer grow in place
+in one run and require a copy in another. CI fixes the threshold at 32 KiB,
+below the 64 KiB upload chunks, to reduce this variation. Allocation, parsing,
+and cleanup stay inside the measured region. Changing this setting shifts
+the simulation baseline, so compare runs that use the same setting.

설명:

이 변경은 로컬 환경에서도 CI와 동일한 glibc 설정을 사용하여 벤치마크를 실행할 수 있도록 안내합니다. 또한, mmap 임계값 조정의 배경과 중요성에 대해 자세히 설명합니다.

  • 문제점 설명: glibc는 이전 할당 기록에 기반하여 mmap 임계값을 동적으로 조정합니다. 이로 인해 특정 실행에서는 업로드 버퍼가 제자리에서(in-place) 확장될 수 있지만, 다른 실행에서는 realloc이 발생하며 데이터를 새로운 메모리 영역으로 복사해야 할 수 있습니다. 이러한 예측 불가능성은 벤치마크 결과의 변동성을 야기합니다.
  • 해결책: CI에서는 이 임계값을 32KB로 고정합니다. 이는 일반적으로 사용되는 64KB 크기의 업로드 청크(chunk)보다 작으므로, 업로드 버퍼가 mmap을 통해 할당될 가능성을 높이고 힙 영역에서의 realloc 및 복사 작업을 줄입니다.
  • 주의사항: 이 설정 변경은 벤치마크의

참고 자료

⚠️ 알림: 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.

댓글

관련 포스트

PR Analysis 의 다른글