GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — gh-150424: Widen specialized int fast paths to full int64 range by KRRT7 · Pull Request #150425 · python/cpython Conversation
|
| Benchmark | main | branch | Change |
|---|---|---|---|
| int_small | 269 ns | — | not significant |
| int_compact | 134 ns | — | not significant |
| int_intermediate_overflow | 1.11 µs | 706 ns | 1.57x faster |
| int_double_add | 1.21 µs | 903 ns | 1.34x faster |
| int_accumulate | 667 ns | 517 ns | 1.29x faster |
| int_always_large | 435 ns | 328 ns | 1.33x faster |
| int_mixed | 155 ns | 118 ns | 1.32x faster |
| Geometric mean | 1.25x faster |
PYTHON_JIT=0)12 numeric/float-heavy benchmarks. Build: PGO + thin LTO, macOS arm64, Homebrew Clang 22.
| Benchmark | main | branch | Change | Significant |
|---|---|---|---|---|
| chaos | 27.2 ms | 27.5 ms | 1.01x slower | No |
| float | 35.6 ms | 35.5 ms | 1.00x faster | No |
| nbody | 51.1 ms | 50.4 ms | 1.01x faster | No |
| pidigits | 228 ms | 228 ms | 1.00x | No |
| pyflate | 212 ms | 218 ms | 1.03x slower | Yes |
| raytrace | 129 ms | 130 ms | 1.00x slower | No |
| scimark_fft | 134 ms | 136 ms | 1.01x slower | No |
| scimark_lu | 54.2 ms | 54.0 ms | 1.00x faster | No |
| scimark_monte_carlo | 31.7 ms | 31.8 ms | 1.00x slower | No |
| scimark_sor | 57.7 ms | 58.1 ms | 1.01x slower | No |
| scimark_sparse_mat_mult | 2.17 ms | 2.13 ms | 1.02x faster | No |
| spectral_norm | 47.4 ms | 46.8 ms | 1.01x faster | No |
PYTHON_JIT=1)| Benchmark | main | branch | Change | Significant |
|---|---|---|---|---|
| chaos | 27.2 ms | 27.6 ms | 1.01x slower | No |
| float | 35.6 ms | 35.5 ms | 1.00x faster | No |
| nbody | 51.2 ms | 50.5 ms | 1.01x faster | No |
| pidigits | 228 ms | 228 ms | 1.00x | No |
| pyflate | 212 ms | 218 ms | 1.03x slower | Yes |
| raytrace | 130 ms | 130 ms | 1.00x slower | No |
| scimark_fft | 135 ms | 136 ms | 1.01x slower | No |
| scimark_lu | 54.1 ms | 54.0 ms | 1.00x faster | No |
| scimark_monte_carlo | 31.7 ms | 31.8 ms | 1.00x slower | No |
| scimark_sor | 57.5 ms | 58.1 ms | 1.01x slower | No |
| scimark_sparse_mat_mult | 2.17 ms | 2.25 ms | 1.04x slower | No |
| spectral_norm | 47.6 ms | 46.7 ms | 1.02x faster | Yes |
pyflate regression (1.03x slower, significant in both modes, t≈−16): likely caused by the `DEOPT_IF(!_PyLong_IsNonNegativeCompact(...))` guards added to `_BINARY_OP_SUBSCR_LIST_INT` / `_STORE_SUBSCR_LIST_INT` in the correctness fix — pyflate does heavy bit-pattern indexing through those bytecodes on every iteration. The guard is necessary for correctness but adds a branch on the hot path that wasn't there before.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Replace the 30-bit compact-only range check (is_medium_int) with __builtin_add_overflow / sub_overflow / mul_overflow, widening the JIT arithmetic fast path from ±2^30 to ±2^62. Relax operand guards from _PyLong_CheckExactAndCompact to PyLong_CheckExact so non-compact inputs also stay in the JIT trace. Add inline _PyLong_AsInt64 for fast digit extraction from non-compact PyLongs (avoids calling the heavy PyLong_AsLongLongAndOverflow). Results (pyperf, ARM64, rigorous): intermediate_overflow 2.70x faster double_add 2.73x faster accumulate 1.65x faster geometric mean 1.52x faster Includes pyperf-based microbenchmark (Tools/scripts/jit_int_benchmark_pyperf.py) and a simpler timeit-based version (jit_int_benchmark.py).
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Do we actually support such builds? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I suspect this has been entirely generated by an LLM (at least the summary has been generated by that). It's unclear what the issue is so please first wait for some feedbcak on the issue before opening the PR. This is the common process as highlighted in our devguide. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Hi, that's incorrect, I'm talking to a core dev directly on the the PR and as noted by the status, it's still a draft and WIP |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
15-bit builds are still supported: --enable-big-digits=15 is a documented configure option, and CPython still has both 15-bit and 30-bit PyLong layouts. I called it out because widening the JIT path to full int64_t exposed a real correctness issue on that https://github.com/python/cpython/blob/main/Doc/using/configure.rst#L229-L237 |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Who's the core dev? Ok for the 15-bit but please, first share a reproducer on the issue before jumping onto PRs. I don't know if you want to address correctness or performance. Those are different. And the described scope is not helpful as there are too many points on the issue. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
I'm addressing performance, the performance changes surfaced correctness issues on 15-bit by the test suite.
I'm working on updating the title and bodies, as I said, this is a draft / WIP, I still haven't settled the final body as I'm doing some cleanup |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Before doing any work, we should first understand the issue. It doesn't make sense to make PRs and update the issue accordingly. This is not how our worklow is. So please, first discuss the change on the issue by including a reproducer. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
I don't think what I'm doing is an "issue", though arguably performance bottlenecks are issues (which is something that I personally believe).
it is however, Github native workflow (opening PRs as a draft, a rough PR where as a draft it is iterated and improved upon), where cpython happens to be hosted in. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
We have a policy written on the devguide. Contributors need to follow that policy. The purpose is to avoid back-and-forth so knowing first what the issue is about is the first step. PRs come after. Nothing forbids you to open something on your fork though. But any change you make in a PR against CPython takes CI resources which affects other contributors. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Considering @Fidget-Spinner's stance, I'm reopening it. But please follow what the devguide says. We have too many AI-generated PRs that take both triagers and reviewers time. When it comes to performance PRs, we really want to see benchmarks before PRs even if in a draft state. This gives more intution on whether the change is worth or not. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Interesting approach! I left some comments for improvement. Could you update the benchmarks to include both the normal and the jit build?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool. If this change has little impact on Python users, wait for a maintainer to apply the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Follow-up from review:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Thanks! Results look good. I am wondering where the performance gain is coming from. I can think of at least three options:
(probably it is a combination of the above). If the fast path for int64 is not contributing to the performance, we could drop it to simplify the code, if it is a significant contribution we could first create a PR adding the I see in one of the recent commits you splitted Could you explore the above? (in separate branches to keep this PR a bit stable) And to make life of reviewers easier: either mark the PR as draft, or keep changes to a minimum. Reviewing a moving target is not easy |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@picnixz I marked it as ready to review because I considered it ready to review, however once I did that, I noticed the CI started to fail which is why I made more changes, let me switch it back to draft, and after that, I'll only mark it as ready for review once we both agree it is in fact, ready for that? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| { | ||
| if (IS_SMALL_INT(x)) { | ||
| return PyStackRef_FromPyObjectBorrow(get_small_int((sdigit)x)); | ||
| if ((b > 0 && a > INT64_MAX - b) || (b < 0 && a < INT64_MIN - b)) { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
There might be faster alternatives to this. See https://stackoverflow.com/a/44830670
(not sure this part is performance critical, and how often we branch to false/true though)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I actually did attempt __builtin_add_overflow but I was having some issues with it, I believe it had to do with cache misses, I'll have to double check
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
I think you can put your PR up to review when it is ready and let Ken Jin shepherd it as he was supportive of the idea (if he wants to) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
oops sorry, I tagged the wrong person here @picnixz , I meant to tag @eendebakpt , got my thoughts messed up, apologies! |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
the compact and wide variants are separate opcodes so that the compact arithmetic path has no https://github.com/KRRT7/cpython/blob/4d495669265/Python/executor_cases.c.h#L4772-L4777 The wide variant ( https://github.com/KRRT7/cpython/blob/4d495669265/Python/executor_cases.c.h#L4938-L4955 If they were one op, every JIT trace for compact int subtraction would carry that dead this was causing a perf regression in my micro benchmarks which is how I caught it |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
…rd substitution - BINARY_OP_ADD_INT (tier1) handles all int64-range ints - Removed all _WIDE tier1 opcodes (no dead code) - Optimizer substitutes _GUARD_TOS_OVERFLOWED when type known as PyLong - Removed _WIDE_INPLACE tier2 ops, merged inplace ops handle both compact and wide - All tests pass
- _GUARD_NOS/TOS_INT use PyLong_CheckExact (accepts all exact ints)
- _BINARY_OP_{ADD,SUBTRACT,MULTIPLY}_INT removed BothAreCompact assert
- Pure ops have ERROR_IF for int64 overflow
- _PyCompactLong_{Add,Subtract,Multiply} branch internally on compactness
- Removed all remaining _WIDE dead code
- Regenerated case files
_GUARD_TOS_INT only checks PyLong_CheckExact, so wide (non-compact) ints can reach _BINARY_OP_SUBSCR_LIST_INT and _STORE_SUBSCR_LIST_INT, both of which call _PyLong_CompactValue unconditionally. The specializer correctly gates on _PyLong_IsNonNegativeCompact, but the runtime guard did not enforce that invariant. Add EXIT_IF / DEOPT_IF(!_PyLong_IsNonNegativeCompact) before each _PyLong_CompactValue call so wide-int indices fall back to the slow path instead of hitting the assertion. The bug was latent before this branch because BINARY_OP_ADD_INT would deopt on wide-int operands, so wide results rarely appeared as list subscripts that had already been specialised for compact ints.
I explored the merged approach you suggested and replaced the split compact/wide design with a single
With the merged design this doesn't happen — the single guard (
I created experiment branches to decompose this. The gain is almost entirely from the custom inline int64 fast path ( Benchmarks (PGO+LTO, clang/LLVM, Apple M-series) against Targeted wide-int microbenchmarks (operands in [2^30, 2^63), the range the fast path covers):
Benchmark scriptimport pyperf BASE = 2**32 # 4_294_967_296 — solidly in wide-int range DELTA = 2**32 + 1 def bench_wide_add(loops): x = BASE y = DELTA for _ in range(loops): x = x + y - y return x def bench_wide_mul(loops): x = BASE y = 3 for _ in range(loops): x = x * y // y return x TS_BASE = 1_748_000_000 # representative Unix timestamp (wide-int range) TS_DELTA = 86400 # one day in seconds (compact) def bench_timestamp_arith(loops): t = TS_BASE d = TS_DELTA for _ in range(loops): t = t + d - d return t runner = pyperf.Runner() runner.bench_func('wide_add', bench_wide_add, 1_000_000) runner.bench_func('wide_mul', bench_wide_mul, 1_000_000) runner.bench_func('timestamp_arith', bench_timestamp_arith, 1_000_000) General pyperformance benchmarks (crypto_pyaes, deltablue, go, hexiom, nqueens): no significant difference on any — all within 1–2% noise, as expected for workloads that don't produce wide-range integers. One correctness fix also landed in this branch: |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Co-authored-by: Pieter Eendebak <[email protected]>
# Conflicts: # Python/executor_cases.c.h
@picnixz, 15-bit builds are supported, of course, but much less tested. We have just one build bot, added recently, see #138336. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
Widen specialized int fast paths to full int64 range
There was an error while loading. Please reload this page.