3P by GN⁺ 12일전 | ★ favorite | 댓글 1개
  • CPython의 테일 콜링(tail-calling) 인터프리터가 Windows x86-64 환경에서 기존 방식보다 약 15% 빠른 성능을 보임
  • macOS AArch64(XCode Clang)에서도 약 5%의 성능 향상이 확인되었으며, Windows에서는 MSVC 2026의 실험적 기능을 활용
  • pyperformance 벤치마크에서 대부분의 테스트가 속도 향상을 보였고, 일부는 최대 78%까지 개선
  • 성능 향상의 주요 원인은 컴파일러 최적화 히유리스틱 리셋과 인라이닝 개선으로 분석됨
  • Python 3.15 정식 릴리스 시, Visual Studio 2026 기반 빌드에서 기본 적용될 예정

테일 콜링 인터프리터의 성능 개선

  • CPython의 테일 콜링 인터프리터가 기존의 switch-case 인터프리터보다 Windows x86-64에서 약 15% 빠른 것으로 측정됨
    • pyperformance 기준, 지오메트릭 평균 15~16% 향상
    • 일부 벤치마크는 최대 78% 속도 향상, 소수의 경우 60% 느려짐
  • macOS AArch64(XCode Clang)에서는 약 5%의 성능 향상 확인
  • 이 결과는 Python 3.15 개발 주기 중 변경이 되지 않는다는 전제하에 유효

인터프리터 구조 비교

  • C 기반 인터프리터 구현 방식은 switch-case, computed goto, tail-call threaded 세 가지로 구분됨
    • switch-case: 명령어별 분기 처리
    • computed goto: GCC/Clang 확장 기능으로, 분기 주소로 직접 점프
    • tail-call threaded: 각 바이트코드 핸들러를 함수로 분리하고, 다음 함수로 테일 콜
  • 과거에는 C 컴파일러가 테일 콜 최적화를 보장하지 않아 스택 오버플로우 위험 존재
  • Clang의 __attribute__((musttail))과 MSVC의 [[msvc::musttail]] 속성으로 강제 테일 콜이 가능해짐

Windows용 MSVC 2026 빌드 결과

  • MSVC의 실험적 기능을 사용한 CPython 빌드에서 벤치마크 대부분이 속도 향상
    • 예시 결과:
      • spectralnorm: 1.48배
      • nbody: 1.35배
      • bm_django_template: 1.18배
      • xdsl: 1.14배
  • Python 3.15의 “What’s New” 문서에 공식 반영됨
    • Visual Studio 2026(MSVC 18) 빌드에서 테일 콜링 인터프리터 사용 가능
    • 순수 Python 라이브러리는 약 15%, 소규모 스크립트는 최대 40% 속도 향상

성능 향상의 원인

  • 테일 콜링은 컴파일러의 최적화 히유리스틱을 초기화하여 더 효율적인 코드 생성을 유도
  • 기존 CPython 인터프리터 루프는 약 12,000라인의 단일 함수로 구성되어 인라이닝 최적화 실패 빈번
    • 컴파일러가 코드 크기 증가를 피하기 위해 인라이닝을 거부하는 사례 다수
  • 테일 콜링 방식에서는 함수가 분리되어 단순 함수들이 인라인 처리 가능
    • 예시로 PyStackRef_CLOSE_SPECIALIZED 같은 간단한 함수가 인라인됨
  • PGO(프로파일 기반 최적화) 빌드에서도 동일한 현상 보고

빌드 및 사용 방법

  • 현재는 소스 빌드만 가능
    • Visual Studio 2026 환경에서 다음 명령으로 빌드
      $env:PlatformToolset = "v145"
      ./PCbuild/build.bat --tail-call-interp -c Release -p x64 --pgo
      
  • 향후 Python 3.15 개발이 안정화되면 공식 바이너리 배포 예정
Hacker News 의견들
  • 블로그 글에 포함되었으면 좋았을 핵심 코드 조각을 공유함
    MSVC와 Clang의 musttailpreserve_none 속성 정의 차이를 보여주는 예시임
    이 속성은 함수 선언자에 붙여야 하며, 함수 지정자 위치에서는 동작하지 않음
    관련 코드 링크
    Microsoft가 중요하다고 판단한 프로젝트에만 이런 비공개 기능을 알려주는 듯한 뉘앙스를 풍김
    • 내가 착각했음. [[msvc::musttail]]은 실제로 공식 문서화된 속성이었음
      블로그 글을 수정해 반영할 예정임
      관련 HN 댓글
    • “중요해서 알려준 걸까, 아니면 자기들에게 이익이 되니까 알려준 걸까?”라는 의문을 제기함
  • 예전 Python 3.14 때처럼 LLVM 19 버그로 인해 잘못된 성능 향상이 보고된 사례를 떠올리며, 이번엔 그런 문제가 없길 바람
    글을 읽어보니 투명성과 빠른 피드백을 우선시한 접근이라 괜찮다고 판단함
    교차 컴파일러 검증이나 독립 감사가 있으면 더 좋겠지만, 저자의 완전한 투명성 덕분에 신뢰할 수 있다고 봄
    • 글쓴이는 그때의 실수가 오히려 행운의 사고였다고 회상함
      일찍 공개했기에 Nelson이 Clang 19 버그를 찾아냈고, 덕분에 정식 릴리스 전에 수정할 수 있었음
      이번엔 dispatch 로직과 inlining 두 가지 개선이 있어 더 자신 있음
      MSVC가 특정 조건에서 switch-case 인터프리터를 threaded code로 바꿀 수 있지만, CPython은 너무 복잡해서 해당 최적화가 적용되지 않음
      대신 tail call 접근법은 C 코드 작성자가 더 많은 제어권을 가짐
      관련 참고: MSVC threaded code 조건, forceinline 관련 이슈
    • 새 설계의 장점은 컴파일러 최적화의 변덕에 덜 의존한다는 점임
      과거엔 tail duplication 같은 최적화가 컴파일러 판단에 따라 달라졌지만, 이제는 인터프리터가 직접 원하는 머신 코드 형태를 표현할 수 있음
      이전 논의 링크
  • 과거 컴파일러 문제를 다룬 발표가 있다며, EuroPython 2025 발표 영상을 공유함
  • 오랜만에 Windows용 GUI 앱을 Python으로 만들고 있음
    C#/MAUI보다 VS 생태계가 너무 무거워서 Python을 선택함
    Tkinter는 불편했고, Qt는 학습 부담이 커서 wxGlade + wxPython 조합을 사용함
    pip로 설치 가능한 단일 의존성만 필요하고, Pythonic한 사용감이 마음에 듦
    Windows 런타임 개선이 반가움
    • 나는 Python + Qt/PySide 조합을 선호함
      QtCreator로 UI를 빠르게 만들고 Python으로 로직을 붙이면 개발 속도가 매우 빠름
    • pyfltk도 추천함. GNU/Linux에서는 꽤 괜찮았음
    • GUI가 중요하다면 LINQPad도 고려할 만함. 스크립팅과 무거운 작업 사이의 중간 지점에 있음
    • Python용 ImGui 바인딩을 추천함
      Tkinter나 Qt처럼 retained mode가 아니라 immediate mode 방식이라 내부 툴링에 특히 유용함
      imgui_bundle 프로젝트
  • “이 정도면 낮은 난이도의 최적화 과제 아닌가?”라며, 왜 아직 인터프리터 루프가 완전히 최적화되지 않았는지 의문을 제기함
    주요 ISA별로 어셈블리로 작성되어 있을 줄 알았다고 함
    • 오히려 이번 업데이트는 루프가 이미 극도로 최적화되어 있음을 보여준다고 생각함
      [[msvc::musttail]]은 MSVC 14.50(지난달 출시)에 추가된 최신 속성인데, CPython 팀이 몇 주 만에 이를 활용해 성능 향상을 이뤘음
      MSVC musttail 문서
    • Python은 애초에 속도보다 단순함을 목표로 함
      Guido가 코드 단순성을 우선시했기에 JIT 도입이 늦었고, 이후 PEP 744(JIT Compilation) 같은 시도가 나옴
    • 오픈소스에 과도한 기대를 하지 말아야 함
      어셈블리 최적화는 유지보수 악몽이며, Python의 진짜 병목은 패키징 시스템임
    • 어차피 성능에 민감한 사람들은 Windows에서 Python을 돌리지 않음이라 언급함
  • “왜 Python 인터프리터는 V8보다 훨씬 느린가?”라는 질문 제기
    • JavaScript는 JIT 컴파일을 사용하지만 CPython은 그렇지 않음
      PyPy는 JIT 덕분에 빠르지만 C 확장과 호환되지 않음
      Python의 스레딩 모델도 최적화를 어렵게 함
      반면 JS는 단일 스레드라 단순함
      Python은 C 확장으로 우회할 수 있어 CPython 자체 최적화에 대한 압박이 적었음
    • Google의 인력과 품질이 큰 차이를 만든다고 봄
      또한 CPython은 방대한 C 확장 생태계 때문에 호환성을 깨지 못함
      반면 V8은 내부 구조를 자유롭게 바꿀 수 있었음
    • Python은 속성 접근조차 descriptor 프로토콜을 거치는 등 훨씬 동적
      또한 안정적인 C ABI를 유지해야 해서 JIT이 자유롭게 코드 분석을 하기 어려움
    • Python은 JS보다 훨씬 동적 언어이고, FFI 바인딩 때문에 내부 변경이 제한됨
      PyPy가 이런 제약을 맞추느라 고생한 사례를 언급함
    • JS는 Google이 웹을 지배하기 위해 막대한 자원을 투입해 최적화했지만, Python은 언어 발전에 더 많은 리소스를 써왔음
      또한 JS는 순수 JS만 지원하면 되지만, Python은 외부 확장 생태계를 유지해야 해서 최적화에 제약이 많음
  • 새로운 벤치마크 그래프가 흥미롭다고 언급하며 어떤 도구로 생성했는지 질문함
    자신은 mitata로 JS 라이브러리 성능을 측정한 경험이 있다고 공유함
    Immer JS 최적화 PR
    • 그 그래프는 violin plot
      Wikipedia 설명, Matplotlib 예시
      분포를 대칭으로 시각화하지만, 스무딩이 실제 분포를 왜곡할 수 있고, 공간 낭비라는 비판도 있음
      HN 논의에서는 half-violin plot이 더 낫다는 의견도 있었음
      예시 이미지
  • Matt Godbolt이 tail-call 기반 인터프리터가 CPU의 분기 예측기(branch predictor) 에 더 잘 맞는다고 언급했음을 전함
  • 글 첫 문장에 오타 두 개가 있는데, 혹시 AI 생성 티를 내기 위한 의도적 실수인지 묻는 댓글임
    • 작성자는 “고마움, 수정했음”이라 답함
    • 다른 사용자는 “AI처럼 보이지 않게 하려면 그냥 직접 쓰면 된다”고 농담 섞인 조언을 함
      AI 글의 전형적인 특징(짧은 문단, 과한 긍정 어조, 깊이 부족 등)을 풍자함
  • “Python 팀이 tail call을 유용하다고 본다면, 언어 자체에도 tail call 지원이 들어올까?”라는 질문을 던짐