From d3fb72a8dcf577edd9b4a97594bd14759dcd2d12 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 16 Jun 2025 22:55:00 +0200 Subject: [PATCH 01/13] x --- Objects/longobject.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 2b533312fee673..e80154cf0762b5 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -511,7 +511,7 @@ PyLong_FromDouble(double dval) long PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) { - /* This version by Tim Peters */ + /* This version originally by Tim Peters */ PyLongObject *v; unsigned long x, prev; long res; @@ -565,6 +565,36 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) goto exit; } } + +/* + digit *digits = v->long_value.ob_digit; + + assert(i >= 2); + #if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1) + /* use 2 digits * + --i; + x = digits[i]; + x <<= PyLong_SHIFT; + --i; + x |= digits[i]; + #else + /* use 1 digit * + //--i; + assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); + //x = digits[i]; + x=0; + #endif +*/ + +/* + while (--i >= 0) { + if (x > SIZE_MAX >> PyLong_SHIFT) { + *overflow = sign; + goto exit; + } + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; + } +*/ /* Haven't lost any bits, but casting to long requires extra * care (see comment above). */ From a38cbfba8f7975cae212b8bbf22c3528a923f85a Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 16 Jun 2025 23:38:32 +0200 Subject: [PATCH 02/13] Improve performance of pylong_as_xxx conversions --- Objects/longobject.c | 87 +++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index ca22574437a270..0154358d384017 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -513,7 +513,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) { /* This version originally by Tim Peters */ PyLongObject *v; - unsigned long x, prev; + unsigned long x; long res; Py_ssize_t i; int sign; @@ -556,37 +556,22 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) res = -1; i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - x = 0; - while (--i >= 0) { - prev = x; - x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; - if ((x >> PyLong_SHIFT) != prev) { - *overflow = sign; - goto exit; - } - } -/* digit *digits = v->long_value.ob_digit; - assert(i >= 2); - #if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1) - /* use 2 digits * + /* unroll 1 digit */ --i; + assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); x = digits[i]; + + #if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1) + /* unroll another digit */ x <<= PyLong_SHIFT; --i; x |= digits[i]; - #else - /* use 1 digit * - //--i; - assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); - //x = digits[i]; - x=0; #endif -*/ -/* + while (--i >= 0) { if (x > SIZE_MAX >> PyLong_SHIFT) { *overflow = sign; @@ -594,7 +579,6 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) } x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } -*/ /* Haven't lost any bits, but casting to long requires extra * care (see comment above). */ @@ -657,7 +641,7 @@ PyLong_AsInt(PyObject *obj) Py_ssize_t PyLong_AsSsize_t(PyObject *vv) { PyLongObject *v; - size_t x, prev; + size_t x; Py_ssize_t i; int sign; @@ -676,12 +660,19 @@ PyLong_AsSsize_t(PyObject *vv) { } i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - x = 0; + + digit *digits = v->long_value.ob_digit; + assert(i >= 2); + /* unroll 1 digit */ + --i; + assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); + x = digits[i]; + while (--i >= 0) { - prev = x; - x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; - if ((x >> PyLong_SHIFT) != prev) + if (x > SIZE_MAX >> PyLong_SHIFT) { goto overflow; + } + x = (x << PyLong_SHIFT) | digits[i]; } /* Haven't lost any bits, but casting to a signed type requires * extra care (see comment above). @@ -707,7 +698,7 @@ unsigned long PyLong_AsUnsignedLong(PyObject *vv) { PyLongObject *v; - unsigned long x, prev; + unsigned long x; Py_ssize_t i; if (vv == NULL) { @@ -738,13 +729,19 @@ PyLong_AsUnsignedLong(PyObject *vv) return (unsigned long) -1; } i = _PyLong_DigitCount(v); - x = 0; + + digit *digits = v->long_value.ob_digit; + assert(i >= 2); + /* unroll 1 digit */ + --i; + assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); + x = digits[i]; + while (--i >= 0) { - prev = x; - x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; - if ((x >> PyLong_SHIFT) != prev) { + if (x > SIZE_MAX >> PyLong_SHIFT) { goto overflow; } + x = (x << PyLong_SHIFT) | digits[i]; } return x; overflow: @@ -761,7 +758,7 @@ size_t PyLong_AsSize_t(PyObject *vv) { PyLongObject *v; - size_t x, prev; + size_t x; Py_ssize_t i; if (vv == NULL) { @@ -783,16 +780,22 @@ PyLong_AsSize_t(PyObject *vv) return (size_t) -1; } i = _PyLong_DigitCount(v); - x = 0; + + digit *digits = v->long_value.ob_digit; + assert(i >= 2); + /* unroll 1 digit */ + --i; + assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); + x = digits[i]; + while (--i >= 0) { - prev = x; - x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; - if ((x >> PyLong_SHIFT) != prev) { - PyErr_SetString(PyExc_OverflowError, - "Python int too large to convert to C size_t"); - return (size_t) -1; + if (x > SIZE_MAX >> PyLong_SHIFT) { + PyErr_SetString(PyExc_OverflowError, + "Python int too large to convert to C size_t"); + return (size_t) -1; + } + x = (x << PyLong_SHIFT) | digits[i]; } - } return x; } From 588490abc32359f02a4e40b1a5f21869ced5f966 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 17 Jun 2025 10:35:07 +0200 Subject: [PATCH 03/13] refactor --- Objects/longobject.c | 71 ++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 0154358d384017..56a24a7adf83f5 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -498,6 +498,26 @@ PyLong_FromDouble(double dval) #define PY_ABS_LONG_MIN (0-(unsigned long)LONG_MIN) #define PY_ABS_SSIZE_T_MIN (0-(size_t)PY_SSIZE_T_MIN) +static inline unsigned long +_unroll_digits(PyLongObject *v, Py_ssize_t *i) +{ + digit *digits = v->long_value.ob_digit; + assert(*digit_count >= 2); + /* unroll 1 digit */ + --(*i); + assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); + unsigned long x = digits[*i]; + + #if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1) + /* unroll another digit */ + x <<= PyLong_SHIFT; + --(*i); + x |= digits[*i]; + #endif + + return x; +} + /* Get a C long int from an int object or any object that has an __index__ method. @@ -507,7 +527,6 @@ PyLong_FromDouble(double dval) For other errors (e.g., TypeError), return -1 and set an error condition. In this case *overflow will be 0. */ - long PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) { @@ -557,21 +576,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - digit *digits = v->long_value.ob_digit; - assert(i >= 2); - /* unroll 1 digit */ - --i; - assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); - x = digits[i]; - - #if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1) - /* unroll another digit */ - x <<= PyLong_SHIFT; - --i; - x |= digits[i]; - #endif - - + x = _unroll_digits(v, &i); while (--i >= 0) { if (x > SIZE_MAX >> PyLong_SHIFT) { *overflow = sign; @@ -661,18 +666,12 @@ PyLong_AsSsize_t(PyObject *vv) { i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - digit *digits = v->long_value.ob_digit; - assert(i >= 2); - /* unroll 1 digit */ - --i; - assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); - x = digits[i]; - + x = _unroll_digits(v, &i); while (--i >= 0) { if (x > SIZE_MAX >> PyLong_SHIFT) { goto overflow; } - x = (x << PyLong_SHIFT) | digits[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } /* Haven't lost any bits, but casting to a signed type requires * extra care (see comment above). @@ -730,18 +729,12 @@ PyLong_AsUnsignedLong(PyObject *vv) } i = _PyLong_DigitCount(v); - digit *digits = v->long_value.ob_digit; - assert(i >= 2); - /* unroll 1 digit */ - --i; - assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); - x = digits[i]; - + x = _unroll_digits(v, &i); while (--i >= 0) { if (x > SIZE_MAX >> PyLong_SHIFT) { goto overflow; } - x = (x << PyLong_SHIFT) | digits[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } return x; overflow: @@ -781,20 +774,14 @@ PyLong_AsSize_t(PyObject *vv) } i = _PyLong_DigitCount(v); - digit *digits = v->long_value.ob_digit; - assert(i >= 2); - /* unroll 1 digit */ - --i; - assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); - x = digits[i]; - + x = _unroll_digits(v, &i); while (--i >= 0) { if (x > SIZE_MAX >> PyLong_SHIFT) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C size_t"); return (size_t) -1; } - x = (x << PyLong_SHIFT) | digits[i]; + x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } return x; } @@ -823,7 +810,7 @@ _PyLong_AsUnsignedLongMask(PyObject *vv) } i = _PyLong_DigitCount(v); int sign = _PyLong_NonCompactSign(v); - x = 0; + x = _unroll_digits(v, &i); while (--i >= 0) { x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } @@ -1642,7 +1629,7 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv) } i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - x = 0; + x = _unroll_digits(v, &i); while (--i >= 0) { x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } From 8031af02bde65e59d911827be79bfbe238fce292 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 17 Jun 2025 10:37:10 +0200 Subject: [PATCH 04/13] fix assert --- Objects/longobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 56a24a7adf83f5..e94b59854fc8d5 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -502,7 +502,7 @@ static inline unsigned long _unroll_digits(PyLongObject *v, Py_ssize_t *i) { digit *digits = v->long_value.ob_digit; - assert(*digit_count >= 2); + assert(*i >= 2); /* unroll 1 digit */ --(*i); assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); From 01c48ae2c8e667f47b795fa3c042a3e4090f40c7 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 08:37:51 +0000 Subject: [PATCH 05/13] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst new file mode 100644 index 00000000000000..d7f42b870e605d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst @@ -0,0 +1 @@ +Improve performance of ``PyLongObject`` conversion method ``PyLong_AsLongAndOverflow``. From 401d860c9b98284d63326c62bf773408608def46 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 17 Jun 2025 16:46:25 +0200 Subject: [PATCH 06/13] Update Objects/longobject.c Co-authored-by: Victor Stinner --- Objects/longobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index e94b59854fc8d5..6391459c1c2aef 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -578,7 +578,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) x = _unroll_digits(v, &i); while (--i >= 0) { - if (x > SIZE_MAX >> PyLong_SHIFT) { + if (x > (ULONG_MAX >> PyLong_SHIFT)) { *overflow = sign; goto exit; } From 750c676dcb5c0e8917c993c8b5b1866ae117f720 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 17 Jun 2025 19:50:25 +0200 Subject: [PATCH 07/13] test --- Objects/longobject.c | 50 +++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 6391459c1c2aef..90af1d3bbfcf57 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -499,7 +499,7 @@ PyLong_FromDouble(double dval) #define PY_ABS_SSIZE_T_MIN (0-(size_t)PY_SSIZE_T_MIN) static inline unsigned long -_unroll_digits(PyLongObject *v, Py_ssize_t *i) +unroll_digits_ulong(PyLongObject *v, Py_ssize_t *i) { digit *digits = v->long_value.ob_digit; assert(*i >= 2); @@ -518,6 +518,26 @@ _unroll_digits(PyLongObject *v, Py_ssize_t *i) return x; } +static inline size_t +unroll_digits_size_t(PyLongObject *v, Py_ssize_t *i) +{ + digit *digits = v->long_value.ob_digit; + assert(*i >= 2); + /* unroll 1 digit */ + --(*i); + assert(SIZE_MAX >= ((1UL << PyLong_SHIFT) - 1)); + size_t x = digits[*i]; + + #if ( (SIZE_MAX >> PyLong_SHIFT) >= ( ( 1 << PyLong_SHIFT) - 1) ) + /* unroll another digit */ + x <<= PyLong_SHIFT; + --(*i); + x |= digits[*i]; + #endif + + return x; +} + /* Get a C long int from an int object or any object that has an __index__ method. @@ -532,7 +552,6 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) { /* This version originally by Tim Peters */ PyLongObject *v; - unsigned long x; long res; Py_ssize_t i; int sign; @@ -576,7 +595,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow) i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - x = _unroll_digits(v, &i); + unsigned long x = unroll_digits_ulong(v, &i); while (--i >= 0) { if (x > (ULONG_MAX >> PyLong_SHIFT)) { *overflow = sign; @@ -646,7 +665,6 @@ PyLong_AsInt(PyObject *obj) Py_ssize_t PyLong_AsSsize_t(PyObject *vv) { PyLongObject *v; - size_t x; Py_ssize_t i; int sign; @@ -666,7 +684,7 @@ PyLong_AsSsize_t(PyObject *vv) { i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - x = _unroll_digits(v, &i); + size_t x = unroll_digits_size_t(v, &i); while (--i >= 0) { if (x > SIZE_MAX >> PyLong_SHIFT) { goto overflow; @@ -697,7 +715,6 @@ unsigned long PyLong_AsUnsignedLong(PyObject *vv) { PyLongObject *v; - unsigned long x; Py_ssize_t i; if (vv == NULL) { @@ -729,9 +746,9 @@ PyLong_AsUnsignedLong(PyObject *vv) } i = _PyLong_DigitCount(v); - x = _unroll_digits(v, &i); + unsigned long x = unroll_digits_ulong(v, &i); while (--i >= 0) { - if (x > SIZE_MAX >> PyLong_SHIFT) { + if (x > ULONG_MAX >> PyLong_SHIFT) { goto overflow; } x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; @@ -751,7 +768,6 @@ size_t PyLong_AsSize_t(PyObject *vv) { PyLongObject *v; - size_t x; Py_ssize_t i; if (vv == NULL) { @@ -774,7 +790,7 @@ PyLong_AsSize_t(PyObject *vv) } i = _PyLong_DigitCount(v); - x = _unroll_digits(v, &i); + size_t x = unroll_digits_size_t(v, &i); while (--i >= 0) { if (x > SIZE_MAX >> PyLong_SHIFT) { PyErr_SetString(PyExc_OverflowError, @@ -793,7 +809,6 @@ static unsigned long _PyLong_AsUnsignedLongMask(PyObject *vv) { PyLongObject *v; - unsigned long x; Py_ssize_t i; if (vv == NULL || !PyLong_Check(vv)) { @@ -810,7 +825,7 @@ _PyLong_AsUnsignedLongMask(PyObject *vv) } i = _PyLong_DigitCount(v); int sign = _PyLong_NonCompactSign(v); - x = _unroll_digits(v, &i); + unsigned long x = unroll_digits_ulong(v, &i); while (--i >= 0) { x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } @@ -1611,7 +1626,6 @@ static unsigned long long _PyLong_AsUnsignedLongLongMask(PyObject *vv) { PyLongObject *v; - unsigned long long x; Py_ssize_t i; int sign; @@ -1629,7 +1643,7 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv) } i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - x = _unroll_digits(v, &i); + unsigned long long x = unroll_digits_ulong(v, &i); while (--i >= 0) { x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; } @@ -1675,7 +1689,6 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) { /* This version by Tim Peters */ PyLongObject *v; - unsigned long long x, prev; long long res; Py_ssize_t i; int sign; @@ -1717,15 +1730,14 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow) else { i = _PyLong_DigitCount(v); sign = _PyLong_NonCompactSign(v); - x = 0; + unsigned long long x = unroll_digits_ulong(v, &i); while (--i >= 0) { - prev = x; - x = (x << PyLong_SHIFT) + v->long_value.ob_digit[i]; - if ((x >> PyLong_SHIFT) != prev) { + if (x > ULLONG_MAX >> PyLong_SHIFT) { *overflow = sign; res = -1; goto exit; } + x = (x << PyLong_SHIFT) + v->long_value.ob_digit[i]; } /* Haven't lost any bits, but casting to long requires extra * care (see comment above). From 73e7b1b11e5bddaeaecfede2d139115df9797eea Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 24 Jun 2025 14:12:29 +0200 Subject: [PATCH 08/13] review comments --- Objects/longobject.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 90af1d3bbfcf57..1c48a054f59a21 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -499,42 +499,46 @@ PyLong_FromDouble(double dval) #define PY_ABS_SSIZE_T_MIN (0-(size_t)PY_SSIZE_T_MIN) static inline unsigned long -unroll_digits_ulong(PyLongObject *v, Py_ssize_t *i) +unroll_digits_ulong(PyLongObject *v, Py_ssize_t *iptr) { + Py_ssize_t i = *iptr; digit *digits = v->long_value.ob_digit; - assert(*i >= 2); + assert(i >= 2); /* unroll 1 digit */ - --(*i); + --i; assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); - unsigned long x = digits[*i]; + unsigned long x = digits[i]; #if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1) /* unroll another digit */ x <<= PyLong_SHIFT; - --(*i); - x |= digits[*i]; + --i; + x |= digits[i]; #endif + *iptr = i; return x; } static inline size_t -unroll_digits_size_t(PyLongObject *v, Py_ssize_t *i) +unroll_digits_size_t(PyLongObject *v, Py_ssize_t *iptr) { + Py_ssize_t i = *iptr; digit *digits = v->long_value.ob_digit; - assert(*i >= 2); + assert(i >= 2); /* unroll 1 digit */ - --(*i); + --i; assert(SIZE_MAX >= ((1UL << PyLong_SHIFT) - 1)); - size_t x = digits[*i]; + size_t x = digits[i]; #if ( (SIZE_MAX >> PyLong_SHIFT) >= ( ( 1 << PyLong_SHIFT) - 1) ) /* unroll another digit */ x <<= PyLong_SHIFT; - --(*i); - x |= digits[*i]; + --i; + x |= digits[i]; #endif + *iptr = i; return x; } From e89d66ce4b4a37455f08ce49ba7eced909804bd0 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 24 Jun 2025 16:17:18 +0200 Subject: [PATCH 09/13] Apply suggestions from code review Co-authored-by: Victor Stinner --- Objects/longobject.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 1c48a054f59a21..f3c1f70cab814d 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -501,20 +501,22 @@ PyLong_FromDouble(double dval) static inline unsigned long unroll_digits_ulong(PyLongObject *v, Py_ssize_t *iptr) { + assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); + Py_ssize_t i = *iptr; - digit *digits = v->long_value.ob_digit; assert(i >= 2); + /* unroll 1 digit */ --i; - assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); + digit *digits = v->long_value.ob_digit; unsigned long x = digits[i]; - #if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1) +#if (ULONG_MAX >> PyLong_SHIFT) >= ((1UL << PyLong_SHIFT) - 1) /* unroll another digit */ x <<= PyLong_SHIFT; --i; x |= digits[i]; - #endif +#endif *iptr = i; return x; @@ -523,12 +525,14 @@ unroll_digits_ulong(PyLongObject *v, Py_ssize_t *iptr) static inline size_t unroll_digits_size_t(PyLongObject *v, Py_ssize_t *iptr) { + assert(SIZE_MAX >= ((1UL << PyLong_SHIFT) - 1)); + Py_ssize_t i = *iptr; - digit *digits = v->long_value.ob_digit; assert(i >= 2); + /* unroll 1 digit */ --i; - assert(SIZE_MAX >= ((1UL << PyLong_SHIFT) - 1)); + digit *digits = v->long_value.ob_digit; size_t x = digits[i]; #if ( (SIZE_MAX >> PyLong_SHIFT) >= ( ( 1 << PyLong_SHIFT) - 1) ) @@ -690,7 +694,7 @@ PyLong_AsSsize_t(PyObject *vv) { size_t x = unroll_digits_size_t(v, &i); while (--i >= 0) { - if (x > SIZE_MAX >> PyLong_SHIFT) { + if (x > (SIZE_MAX >> PyLong_SHIFT)) { goto overflow; } x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; @@ -752,7 +756,7 @@ PyLong_AsUnsignedLong(PyObject *vv) unsigned long x = unroll_digits_ulong(v, &i); while (--i >= 0) { - if (x > ULONG_MAX >> PyLong_SHIFT) { + if (x > (ULONG_MAX >> PyLong_SHIFT)) { goto overflow; } x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i]; @@ -796,7 +800,7 @@ PyLong_AsSize_t(PyObject *vv) size_t x = unroll_digits_size_t(v, &i); while (--i >= 0) { - if (x > SIZE_MAX >> PyLong_SHIFT) { + if (x > (SIZE_MAX >> PyLong_SHIFT)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C size_t"); return (size_t) -1; From 7822211527241febb513f395070f770f26149a54 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 24 Jun 2025 16:20:41 +0200 Subject: [PATCH 10/13] update news entry --- .../2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst index d7f42b870e605d..b5f6acd343a1e3 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst @@ -1 +1,4 @@ -Improve performance of ``PyLongObject`` conversion method ``PyLong_AsLongAndOverflow``. +Improve performance of ``PyLongObject`` conversion methods ``PyLong_AsLongAndOverflow``, +``PyLong_AsLongAndOverflow()``, ``PyLong_AsSsize_t()``, ``PyLong_AsUnsignedLong()``, ``PyLong_AsSize_t()``, +``PyLong_AsUnsignedLongMask()``, ``PyLong_AsUnsignedLongLongMask()``, ``PyLong_AsLongLongAndOverflow()`` up to 30%. + From f4793e2c2ea9317ef12f6296fee4b15ef83b3b9e Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 24 Jun 2025 16:22:51 +0200 Subject: [PATCH 11/13] reword --- .../2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst index b5f6acd343a1e3..e6ead578cc4a1a 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst @@ -1,4 +1,5 @@ Improve performance of ``PyLongObject`` conversion methods ``PyLong_AsLongAndOverflow``, ``PyLong_AsLongAndOverflow()``, ``PyLong_AsSsize_t()``, ``PyLong_AsUnsignedLong()``, ``PyLong_AsSize_t()``, -``PyLong_AsUnsignedLongMask()``, ``PyLong_AsUnsignedLongLongMask()``, ``PyLong_AsLongLongAndOverflow()`` up to 30%. +``PyLong_AsUnsignedLongMask()``, ``PyLong_AsUnsignedLongLongMask()``, ``PyLong_AsLongLongAndOverflow()`` +for integers larger than 2**30 up to 30%. From 1fc8706809728c142ca190985f1e5f973e349a4c Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 24 Jun 2025 16:32:12 +0200 Subject: [PATCH 12/13] lint --- Objects/longobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 707bc42f52918f..6d5748e129ba30 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -530,7 +530,7 @@ static inline unsigned long unroll_digits_ulong(PyLongObject *v, Py_ssize_t *iptr) { assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1)); - + Py_ssize_t i = *iptr; assert(i >= 2); From 2d9d09834bc9ab9b64cc4b7ffcf763319642565a Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Tue, 24 Jun 2025 20:44:50 +0200 Subject: [PATCH 13/13] Apply suggestions from code review Co-authored-by: Victor Stinner --- .../2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst | 2 +- Objects/longobject.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst index e6ead578cc4a1a..74bd182c1990f8 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-08-37-45.gh-issue-82088.TgPvLg.rst @@ -1,4 +1,4 @@ -Improve performance of ``PyLongObject`` conversion methods ``PyLong_AsLongAndOverflow``, +Improve performance of ``PyLongObject`` conversion functions ``PyLong_AsLongAndOverflow()``, ``PyLong_AsSsize_t()``, ``PyLong_AsUnsignedLong()``, ``PyLong_AsSize_t()``, ``PyLong_AsUnsignedLongMask()``, ``PyLong_AsUnsignedLongLongMask()``, ``PyLong_AsLongLongAndOverflow()`` for integers larger than 2**30 up to 30%. diff --git a/Objects/longobject.c b/Objects/longobject.c index 6d5748e129ba30..581db10b54ab57 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -563,12 +563,12 @@ unroll_digits_size_t(PyLongObject *v, Py_ssize_t *iptr) digit *digits = v->long_value.ob_digit; size_t x = digits[i]; - #if ( (SIZE_MAX >> PyLong_SHIFT) >= ( ( 1 << PyLong_SHIFT) - 1) ) +#if (SIZE_MAX >> PyLong_SHIFT) >= ((1 << PyLong_SHIFT) - 1) /* unroll another digit */ x <<= PyLong_SHIFT; --i; x |= digits[i]; - #endif +#endif *iptr = i; return x; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy