python / python/cpython

`TO_BOOL_INT` repeatedly misses for non-compact exact integers

Đang mở
#155,486 4 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

interpreter-core performance type-bug
Ngôn ngữ chính
Python
Star
77.2k
Fork
35.9k
Chỉ số merge pull request
Chỉ số pull request đang chờ

Mô tả

Feature or enhancement

Proposal:

Versions

CPython 3.15.0b4, Ubuntu 24.04.4 LTS, gcc 13.3.0

Enhancement

I noticed that the specialization function for TO_BOOL and the guard used by TO_BOOL_INT accept different sets of integers.

_Py_Specialize_ToBool() selects TO_BOOL_INT for any exact integer:

if (PyLong_CheckExact(value)) {
    specialized_op = TO_BOOL_INT;
    goto success;
}

However, the TO_BOOL_INT macro uses _GUARD_TOS_INT, and that requires the integer to be compact:

op(_GUARD_TOS_INT, (value -- value)) {
    PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
    EXIT_IF(!_PyLong_CheckExactAndCompact(value_o));
}

As a result, a non-compact exact integer can cause TO_BOOL_INT to be selected and then immediately miss its guard.

This mismatch appears to have been introduced by GH-143759. Before the refactoring, TO_BOOL_INT had its own PyLong_CheckExact() guard. The refactoring replaced it with a macro using the shared _GUARD_TOS_INT, whose domain is narrower.

I see two alternative ways to fix this.

Option 1: narrow the specialization function

One option would be to change the integer check in _Py_Specialize_ToBool() so that it agrees with the existing guard:

if (_PyLong_CheckExactAndCompact(value)) {
    specialized_op = TO_BOOL_INT;
    goto success;
}

With this change, non-compact integers remain on the generic TO_BOOL path instead of repeatedly entering and missing TO_BOOL_INT.

My main concern with this option was whether the additional compactness check in the specialization function could regress the common case (compact int), so I benchmarked both compact and non-compact integers.

The benchmark target contained 100 TO_BOOL sites and was warmed up before each measurement:

start = time.perf_counter_ns()
for _ in range(10_000):
    target(value)
elapsed = time.perf_counter_ns() - start

Positive values mean that the patched build was faster:

Version Input Performance change
CPython 3.15 non-compact exact int +2.21% (95% CI: +1.64% to +2.86%)
CPython 3.15 compact exact int +0.31% (95% CI: −0.15% to +0.70%)
CPython main non-compact exact int +4.03% (95% CI: +1.43% to +7.10%)
CPython main compact exact int −0.23% (95% CI: −0.50% to +0.07%)

The non-compact case improved because it no longer repeatedly enters and misses TO_BOOL_INT. For compact integers, both confidence intervals include zero, so I did not find evidence that the stronger specialization check causes a regression.

Option 2: give TO_BOOL_INT an exact-int guard

The other option is to keep _Py_Specialize_ToBool() unchanged and add a guard that checks exact type without requiring compactness:

op(_GUARD_TOS_EXACT_INT, (value -- value)) {
    PyObject *value_o = PyStackRef_AsPyObjectBorrow(value);
    EXIT_IF(!PyLong_CheckExact(value_o));
}

The TO_BOOL_INT macro would then use the new guard:

macro(TO_BOOL_INT) =
    _GUARD_TOS_EXACT_INT +
    unused/1 +
    unused/2 +
    _TO_BOOL_INT +
    _POP_TOP_INT;

This would restore the specialization domain from before GH-143759.


I am not sure which of these two options is preferable, but the current mismatch seems worth fixing, so I am opening this issue to get feedback on which direction would be better.

Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

No response

Linked PRs
  • gh-155531
  • gh-155699
  • gh-155700

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu với Python/specialize.c tại _Py_Specialize_ToBool() và Python/bytecodes.c tại TO_BOOL_INT cùng _GUARD_TOS_INT. Xem lại các PR được liên kết gh-155531, gh-155699 và gh-155700, sau đó tái hiện benchmark warm-up đã nêu; hoàn tất khi các miền specialization và guard nhất quán với nhau mà không xảy ra miss lặp lại.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
c, python
Lĩnh vực
compilers, performance
Loại issue
Tính năng
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
35/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.