Python 3.14 tail-call(꼬리 호출) 인터프리터의 성능

4 days ago 3

  • CPython 프로젝트는 최근 바이트코드 인터프리터의 새로운 구현 전략을 도입. 초기 결과는 다양한 플랫폼에서 평균 10-15%의 성능 향상을 보여줬음
  • 그러나 이 성능 향상은 주로 LLVM 19의 회귀 문제를 우회한 결과였음. 더 나은 기준(예: GCC, clang-18, 특정 튜닝 플래그가 있는 LLVM 19)과 비교했을 때 성능 향상은 1-5%로 감소

성능 결과

  • 여러 컴파일러와 구성 옵션을 사용하여 CPython 인터프리터의 여러 빌드를 벤치마크했음. Intel 서버와 Apple M1 Macbook Air에서 테스트했음.
  • 모든 빌드는 LTO와 PGO를 사용했음. clang18을 기준으로 pypeformance/pyperf compare_to에서 보고된 평균을 사용했음.
  • 컴파일러 성능 비교
    • Apple M1 Macbook Air :
      • clang18: 기준
      • clang19: 1.12배 느림
      • clang19.taildup: 1.02배 느림
      • clang19.tc: 1.00배 느림
      • gcc: N/A
  • 꼬리 호출 인터프리터는 여전히 clang-18과 비교하여 속도 향상을 보였지만, clang-19로 이동하면서의 속도 저하가 더 극적이었음.

LLVM 회귀

간단한 배경

  • 전통적인 바이트코드 인터프리터는 while 루프 내의 switch 문으로 구성됨. 대부분의 컴파일러는 switch를 점프 테이블로 컴파일함.
  • 현대 C 컴파일러는 레이블의 주소를 취하고 이를 "계산된 goto"로 사용하는 패턴을 지원함. CPython은 꼬리 호출 작업 전까지 이 패턴을 사용했음.

LLVM 19 회귀

  • LLVM 19는 tail-duplication 패스에 제한을 두어, IR 크기가 특정 한계를 초과할 경우 중복을 중단하도록 했음. 이로 인해 CPython에서는 모든 디스패치 점프가 병합되어 계산된 goto 기반 구현의 목적이 완전히 무산됨.

추가 이상 현상

  • 꼬리 호출 중복 논리의 변경이 회귀를 초래했음을 확신하지만, 회귀의 크기를 완전히 설명할 수는 없음.
  • 현대 프로세서에서는 2-4%의 속도 향상이 더 일반적임.

계산된 goto가 필요한가?

  • clang19.nocg 벤치마크는 clang19보다 빠르다고 주장함. 이는 컴파일러가 switch 기반 인터프리터를 사용하여 동일한 최적화를 수행할 수 있음을 보여줌.

수정

  • LLVM 풀 리퀘스트 114990이 회귀를 수정했음. 이 수정은 예상 성능을 복원함.

반성

벤치마킹에 대하여

  • 시스템 최적화 시 벤치마크와 벤치마킹 방법론을 구성하고, 제안된 변경 사항을 평가함.
  • 벤치마크는 특정 데이터 포인트를 일반화하기 위해 더 많은 가정과 믿음을 필요로 함.

기준선

  • 새로운 솔루션이나 방법을 제안할 때, "현재 가장 잘 알려진 접근 방식"과 비교하는 것이 일반적임.

소프트웨어 엔지니어링에 대하여

  • 소프트웨어 시스템은 복잡하고 상호 연결되어 있으며, 빠르게 변화하고 있음.
  • 최적화 컴파일러는 프로그래머의 의도를 존중하면서도 코드를 최적화해야 하는 긴장 관계에 있음.

최적화 컴파일러

  • musttail 속성은 최적화와 관련된 새로운 종류의 컴파일러 기능을 나타냄. 이는 성능에 민감한 코드를 작성하는 데 더 강력한 스타일을 제공할 수 있음.

nix에 대한 한 가지 더

  • nix는 이 프로젝트에서 매우 유용했음. 여러 버전의 Python 인터프리터를 관리하고 빌드하는 데 큰 도움이 되었음.

Read Entire Article