[cpython] CPython 성능 최적화: re.split의 리스트 빌드 과정 개선하기
PR 링크: python/cpython#155940 상태: Merged | 변경: +3 / -6
들어가며
Python의 표준 라이브러리인 re 모듈은 텍스트 처리에 필수적입니다. 하지만 CPython 내부 구현에서 리스트를 생성하는 방식은 때때로 불필요한 오버헤드를 발생시킵니다. 이번에 살펴볼 PR(gh-150942)은 re.split 함수가 결과를 리스트에 담는 과정에서 발생하는 참조 카운트(Reference Count) 조작의 비효율성을 해결하여 성능을 개선했습니다. 특히 최근 도입된 Free-threaded CPython 빌드에서 더욱 유의미한 성능 향상을 보여줍니다.
코드 분석: _PyList_AppendTakeRef의 도입
기존의 re.split 구현은 PyList_Append를 호출한 뒤, 생성된 객체의 참조 카운트를 감소시키기 위해 Py_DECREF를 명시적으로 호출해야 했습니다. 이는 CPU 사이클을 낭비할 뿐만 아니라, 멀티스레드 환경(특히 Free-threaded 빌드)에서는 리스트 객체에 대한 락(Lock)을 두 번 획득/해제하게 만드는 원인이 되었습니다.
Modules/_sre/sre.c 변경 사항
핵심 변경은 PyList_Append와 Py_DECREF 조합을 _PyList_AppendTakeRef로 대체하는 것입니다. _PyList_AppendTakeRef는 이름에서 알 수 있듯이, 전달받은 객체의 참조(Reference)를 리스트가 '가져가는(Take)' 방식입니다.
Before:
status = PyList_Append(list, item);
Py_DECREF(item);
After:
status = _PyList_AppendTakeRef((PyListObject *)list, item);
이 변경은 _sre_SRE_Pattern_split_impl 내의 모든 리스트 빌드 루프에 적용되었습니다. _PyList_AppendTakeRef를 사용하면 내부적으로 참조 카운트를 추가로 조작할 필요가 없으므로, 코드의 간결함은 물론 성능 최적화까지 달성할 수 있습니다.
왜 이게 좋은가?
이 최적화의 핵심은 '불필요한 참조 카운트 라운드 트립 제거'입니다.
- 성능 향상: 마이크로 벤치마크 결과, 일반적인 CPython 빌드에서는 약 3%의 성능 향상을 보였으며, 특히 Free-threaded 빌드에서는 최대 18%(
re_split_groups)까지 성능이 향상되었습니다. - 락 경합 감소: Free-threaded 빌드에서는 리스트 객체에 대한 락을 한 번만 획득하면 되므로, 멀티스레드 환경에서의 확장성이 개선됩니다.
- 안전성:
_PyList_AppendTakeRef는 실패 시에도 참조를 적절히 처리하므로, 기존의error:레이블로 이동하는 에러 처리 로직을 수정할 필요가 없습니다.
일반적 교훈
CPython과 같은 저수준 C 확장 모듈을 작성할 때, 객체의 생명주기를 관리하는 참조 카운트 조작은 성능에 큰 영향을 미칩니다. 가능한 경우 TakeRef 계열의 API를 활용하여 불필요한 Py_INCREF/Py_DECREF 호출을 줄이는 것이 성능 최적화의 중요한 전략입니다.
참고 자료
참고 자료
⚠️ 알림: 이 분석은 AI가 실제 코드 diff를 기반으로 작성했습니다.
관련 포스트
- [cpython] CPython `PyFloat_Pack/Unpack2` 최적화: 네이티브 `_Float16` 활용으로 성능 향상
- [cpython] Python 문자열 split/splitlines 성능 개선: _PyList_AppendTakeRef 도입
- [cpython] CPython 최적화: functools.reduce 대신 Union 직접 사용하기
- [cpython] CPython unicodedata.normalize() 최적화: Py_UCS4 버퍼 직접 조작으로 성능 향상
- [cpython] Python re 모듈의 findall, sub, subn 성능 개선: PyList_AppendTakeRef 도입
PR Analysis 의 다른글
- 이전글 [cpython] Python 문자열 split/splitlines 성능 개선: _PyList_AppendTakeRef 도입
- 현재글 : [cpython] CPython 성능 최적화: re.split의 리스트 빌드 과정 개선하기
- 다음글 [sglang] MiniMax-H3 모델을 24GB GPU에서 가속화하는 INT8 양자화 및 플러그형 어텐션 최적화
댓글