Github Actions, 벤치마크 결과가 러너 환경에 따라 달라진다고?
CodSpeed는 벤치마크 회귀(Regression) 현상의 원인을 찾기 위해 Github Actions 러너 환경을 분석함
Valgrind를 사용한 벤치마크 환경에서 CPU 및 캐시(Cache) 크기에 따라 성능 차이가 발생함을 확인
glibc가 CPU의 특징을 감지하여 최적화하기 때문에, 러너 환경 변화가 성능 변동을 야기함
매크로 러너(Macro Runner) 사용 또는 GLIBC_TUNABLES 설정을 통해 변동성을 줄일 수 있음
Github Actions 러너 환경의 다양성
Github Actions 러너는 다양한 CPU(Central Processing Unit)를 사용하며, 이는 벤치마크 결과의 변동성을 야기한다. 특히, Intel Xeon과 AMD EPYC CPU 간의 캐시 크기(Cache Size) 및 지원하는 명령어 집합(Instruction Set)의 차이가 glibc의 성능에 영향을 미친다. CodSpeed는 이러한 환경 차이를 확인하기 위해 벤치마크를 여러 번 실행하고, 각 실행 결과의 차이를 분석했다.
glibc의 CPU 특징 감지 및 최적화
glibc는 런타임(Runtime)에 CPU의 특징을 감지하여 성능을 최적화한다. 예를 들어, CPU 코어(Core) 수를 감지하여 락 경합(Lock Contention)을 줄이고, 캐시 크기를 감지하여 비-템포럴 명령어(Non-Temporal Instructions)를 사용하여 캐시 트래싱(Cache Trashing)을 방지한다. 이러한 최적화는 CPU의 성능을 최대한 활용하지만, 러너 환경이 변경되면 벤치마크 결과의 변동성을 증가시킬 수 있다.
벤치마크 결과의 변동성 해결 방안
벤치마크 결과의 변동성을 해결하기 위해, CodSpeed는 매크로 러너(Macro Runner)를 사용하여 동일한 CPU 환경을 보장하거나, GLIBC_TUNABLES 환경 변수를 설정하여 glibc의 특정 기능 사용을 제한하는 방법을 제시한다. GLIBC_TUNABLES는 CPU 특징 감지를 비활성화하여 변동성을 줄일 수 있지만, 각 CPU 특징별로 설정을 해야 하는 번거로움이 있다. 또한, Callgrind를 수정하여 CPU 특징을 '스푸핑(Spoofing)'하는 방법도 고려할 수 있다.
Valgrind를 이용한 벤치마킹
CodSpeed는 Valgrind의 Callgrind 도구를 사용하여 벤치마크를 수행하고, 캐시 미스(Cache Miss), 데이터 읽기/쓰기 등 다양한 성능 지표를 측정한다. Callgrind는 가상 CPU(Virtual CPU)에서 코드를 실행하므로, 이론적으로는 실행 결과가 완전히 결정적(Deterministic)이어야 한다. 하지만, 디스크 읽기, 네트워크 호출, 시스템 콜(System Call) 등 외부 요인으로 인해 변동성이 발생할 수 있다. 따라서, CodSpeed는 벤치마크를 여러 번 실행하여 변동성을 분석한다.