From 7b11d7d64675b5d84c6a35ba2e18783b27227d58 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Sat, 11 Dec 2021 12:05:54 +0800 Subject: [PATCH 01/16] Perf: Accelerate binary shift op. --- Objects/longobject.c | 96 +++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 32 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index f6d5e7648be16f..f36165c47ac140 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -32,7 +32,9 @@ static inline stwodigits medium_value(PyLongObject *x) { assert(IS_MEDIUM_VALUE(x)); - return ((stwodigits)Py_SIZE(x)) * x->ob_digit[0]; + Py_ssize_t s = Py_SIZE(x); + stwodigits d = x->ob_digit[0]; + return s == 1 ? d : s == -1 ? -d : 0; } #define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS) @@ -45,7 +47,7 @@ static inline int is_medium_int(stwodigits x) return x_plus_mask < ((twodigits)PyLong_MASK) + PyLong_BASE; } -static PyObject * +static inline PyObject * get_small_int(sdigit ival) { assert(IS_SMALL_INT(ival)); @@ -54,7 +56,7 @@ get_small_int(sdigit ival) return v; } -static PyLongObject * +static inline PyLongObject * maybe_small_long(PyLongObject *v) { if (v && IS_MEDIUM_VALUE(v)) { @@ -4491,38 +4493,58 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) { PyLongObject *z = NULL; Py_ssize_t newsize, hishift, i, j; - digit lomask, himask; + digit lomask, himask, omitmark; - if (Py_SIZE(a) < 0) { - /* Right shifting negative numbers is harder */ - PyLongObject *a1, *a2; - a1 = (PyLongObject *) long_invert(a); - if (a1 == NULL) - return NULL; - a2 = (PyLongObject *) long_rshift1(a1, wordshift, remshift); - Py_DECREF(a1); - if (a2 == NULL) - return NULL; - z = (PyLongObject *) long_invert(a2); - Py_DECREF(a2); + /* for a positive integrate x + -x >> m = -(x >> m) when x is two's pow + = -(x >> m) -1 otherwise */ + + if (IS_MEDIUM_VALUE(a)) { + stwodigits sval; + if (wordshift > 0) + return get_small_int((sdigit)Py_SIZE(a) >> 1); + + sval = medium_value(a) >> remshift; + if (IS_SMALL_INT(sval)) + return get_small_int((sdigit)sval); + + z = _PyLong_New(Py_SIZE(a)); + z->ob_digit[0] = a->ob_digit[0] >> remshift; + if (Py_SIZE(a) < 0 && (a->ob_digit[0] & ~(PyLong_MASK << remshift))) + z->ob_digit[0] += 1; + return (PyObject *)z; } - else { - newsize = Py_SIZE(a) - wordshift; - if (newsize <= 0) - return PyLong_FromLong(0); - hishift = PyLong_SHIFT - remshift; - lomask = ((digit)1 << hishift) - 1; - himask = PyLong_MASK ^ lomask; - z = _PyLong_New(newsize); - if (z == NULL) - return NULL; - for (i = 0, j = wordshift; i < newsize; i++, j++) { - z->ob_digit[i] = (a->ob_digit[j] >> remshift) & lomask; - if (i+1 < newsize) - z->ob_digit[i] |= (a->ob_digit[j+1] << hishift) & himask; + + newsize = Py_ABS(Py_SIZE(a)) - wordshift; + if (newsize <= 0) + return PyLong_FromLong(0); + z = _PyLong_New(newsize); + if (z == NULL) + return NULL; + + hishift = PyLong_SHIFT - remshift; + lomask = ((digit)1 << hishift) - 1; + himask = PyLong_MASK ^ lomask; + for (i = 0, j = wordshift; i < newsize; i++, j++) { + z->ob_digit[i] = (a->ob_digit[j] >> remshift) & lomask; + if (i + 1 < newsize) + z->ob_digit[i] |= (a->ob_digit[j + 1] << hishift) & himask; + } + + if (Py_SIZE(a) < 0) { + Py_SET_SIZE(z, -newsize); + omitmark = a->ob_digit[wordshift] & ((1 << remshift) - 1); + for (j = 0; omitmark == 0 && j < wordshift; j++) + omitmark |= a->ob_digit[j]; + if (omitmark) { + Py_DECREF(z); + z = (PyLongObject *) long_sub(z, (PyLongObject *)_PyLong_GetOne()); + if (z == NULL) + return NULL; } - z = maybe_small_long(long_normalize(z)); } + + z = maybe_small_long(long_normalize(z)); return (PyObject *)z; } @@ -4565,11 +4587,21 @@ _PyLong_Rshift(PyObject *a, size_t shiftby) static PyObject * long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) { - /* This version due to Tim Peters */ PyLongObject *z = NULL; Py_ssize_t oldsize, newsize, i, j; twodigits accum; + if (wordshift == 0 && IS_MEDIUM_VALUE(a) && + (a->ob_digit[0] & ~(PyLong_MASK >> remshift)) == 0) { + stwodigits sval = medium_value(a) << remshift; + if (IS_SMALL_INT(sval)) + return get_small_int((sdigit)sval); + z = _PyLong_New(Py_SIZE(a)); + z->ob_digit[0] = a->ob_digit[0] << remshift; + return (PyObject *)z; + } + + /* This version due to Tim Peters */ oldsize = Py_ABS(Py_SIZE(a)); newsize = oldsize + wordshift; if (remshift) From 403652a9672037daaa7dfd013009a0be645ac845 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Sun, 12 Dec 2021 12:59:26 +0800 Subject: [PATCH 02/16] Fix: Ubuntu gcc segment fault. --- Objects/longobject.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index f36165c47ac140..4a00a5a13f79cb 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4508,8 +4508,9 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (IS_SMALL_INT(sval)) return get_small_int((sdigit)sval); - z = _PyLong_New(Py_SIZE(a)); - z->ob_digit[0] = a->ob_digit[0] >> remshift; + z = _PyLong_New(Py_ABS(Py_SIZE(a))); + Py_SET_SIZE(z, Py_SIZE(a)); + z->ob_digit[0] = (digit)(a->ob_digit[0] >> remshift); if (Py_SIZE(a) < 0 && (a->ob_digit[0] & ~(PyLong_MASK << remshift))) z->ob_digit[0] += 1; return (PyObject *)z; @@ -4537,8 +4538,9 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) for (j = 0; omitmark == 0 && j < wordshift; j++) omitmark |= a->ob_digit[j]; if (omitmark) { - Py_DECREF(z); - z = (PyLongObject *) long_sub(z, (PyLongObject *)_PyLong_GetOne()); + PyLongObject *z0 = z; + z = (PyLongObject *) long_sub(z0, (PyLongObject *)_PyLong_GetOne()); + Py_DECREF(z0); if (z == NULL) return NULL; } @@ -4596,8 +4598,9 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) stwodigits sval = medium_value(a) << remshift; if (IS_SMALL_INT(sval)) return get_small_int((sdigit)sval); - z = _PyLong_New(Py_SIZE(a)); - z->ob_digit[0] = a->ob_digit[0] << remshift; + z = _PyLong_New(Py_ABS(Py_SIZE(a))); + Py_SET_SIZE(z, Py_SIZE(a)); + z->ob_digit[0] = (digit)(a->ob_digit[0] << remshift); return (PyObject *)z; } From dcd27965954068aaa79b0a84bfae807bea5f1d58 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Sun, 12 Dec 2021 13:18:41 +0800 Subject: [PATCH 03/16] Let PyLong_New accept a negtive size. --- Objects/longobject.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 4a00a5a13f79cb..61405db0eae41d 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -116,14 +116,15 @@ PyLongObject * _PyLong_New(Py_ssize_t size) { PyLongObject *result; - if (size > (Py_ssize_t)MAX_LONG_DIGITS) { + Py_ssize_t size_abs = Py_ABS(size); + if (size_abs > (Py_ssize_t)MAX_LONG_DIGITS) { PyErr_SetString(PyExc_OverflowError, "too many digits in integer"); return NULL; } /* Fast operations for single digit integers (including zero) * assume that there is always at least one digit present. */ - Py_ssize_t ndigits = size ? size : 1; + Py_ssize_t ndigits = size_abs ? size_abs : 1; /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) + sizeof(digit)*size. Previous incarnations of this code used sizeof(PyVarObject) instead of the offsetof, but this risks being @@ -4508,8 +4509,7 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (IS_SMALL_INT(sval)) return get_small_int((sdigit)sval); - z = _PyLong_New(Py_ABS(Py_SIZE(a))); - Py_SET_SIZE(z, Py_SIZE(a)); + z = _PyLong_New(Py_SIZE(a)); z->ob_digit[0] = (digit)(a->ob_digit[0] >> remshift); if (Py_SIZE(a) < 0 && (a->ob_digit[0] & ~(PyLong_MASK << remshift))) z->ob_digit[0] += 1; @@ -4598,8 +4598,7 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) stwodigits sval = medium_value(a) << remshift; if (IS_SMALL_INT(sval)) return get_small_int((sdigit)sval); - z = _PyLong_New(Py_ABS(Py_SIZE(a))); - Py_SET_SIZE(z, Py_SIZE(a)); + z = _PyLong_New(Py_SIZE(a)); z->ob_digit[0] = (digit)(a->ob_digit[0] << remshift); return (PyObject *)z; } From 25bbf4de2f11c0177c8e5f2c27338a8ea6182730 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Sun, 12 Dec 2021 20:04:35 +0800 Subject: [PATCH 04/16] Fix: Use Py_ARITHMETIC_RIGHT_SHIFT to avoid the undefined padding bahavior of C right shift operator. --- Objects/longobject.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 61405db0eae41d..b4edf7e86601ee 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4497,15 +4497,15 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) digit lomask, himask, omitmark; /* for a positive integrate x - -x >> m = -(x >> m) when x is two's pow - = -(x >> m) -1 otherwise */ + -x >> m = -(x >> m) when the dropped bits are all zeros, + = -(x >> m) -1 otherwise. */ if (IS_MEDIUM_VALUE(a)) { stwodigits sval; if (wordshift > 0) - return get_small_int((sdigit)Py_SIZE(a) >> 1); + return get_small_int(-(Py_SIZE(a) < 0)); - sval = medium_value(a) >> remshift; + sval = Py_ARITHMETIC_RIGHT_SHIFT(stwodigits, medium_value(a), remshift); if (IS_SMALL_INT(sval)) return get_small_int((sdigit)sval); From b2ef93da5083531d2f7dd5c1d7eb08523a57ee30 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Sun, 12 Dec 2021 20:10:25 +0800 Subject: [PATCH 05/16] Style: PEP7: add braces. --- Objects/longobject.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index b4edf7e86601ee..1b00f1f36c9ace 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4502,47 +4502,53 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (IS_MEDIUM_VALUE(a)) { stwodigits sval; - if (wordshift > 0) + if (wordshift > 0) { return get_small_int(-(Py_SIZE(a) < 0)); - + } sval = Py_ARITHMETIC_RIGHT_SHIFT(stwodigits, medium_value(a), remshift); - if (IS_SMALL_INT(sval)) + if (IS_SMALL_INT(sval)) { return get_small_int((sdigit)sval); - + } z = _PyLong_New(Py_SIZE(a)); z->ob_digit[0] = (digit)(a->ob_digit[0] >> remshift); - if (Py_SIZE(a) < 0 && (a->ob_digit[0] & ~(PyLong_MASK << remshift))) + if (Py_SIZE(a) < 0 && (a->ob_digit[0] & ~(PyLong_MASK << remshift))) { z->ob_digit[0] += 1; + } return (PyObject *)z; } newsize = Py_ABS(Py_SIZE(a)) - wordshift; - if (newsize <= 0) + if (newsize <= 0) { return PyLong_FromLong(0); + } z = _PyLong_New(newsize); - if (z == NULL) + if (z == NULL) { return NULL; + } hishift = PyLong_SHIFT - remshift; lomask = ((digit)1 << hishift) - 1; himask = PyLong_MASK ^ lomask; for (i = 0, j = wordshift; i < newsize; i++, j++) { z->ob_digit[i] = (a->ob_digit[j] >> remshift) & lomask; - if (i + 1 < newsize) + if (i + 1 < newsize) { z->ob_digit[i] |= (a->ob_digit[j + 1] << hishift) & himask; + } } if (Py_SIZE(a) < 0) { Py_SET_SIZE(z, -newsize); omitmark = a->ob_digit[wordshift] & ((1 << remshift) - 1); - for (j = 0; omitmark == 0 && j < wordshift; j++) + for (j = 0; omitmark == 0 && j < wordshift; j++) { omitmark |= a->ob_digit[j]; + } if (omitmark) { PyLongObject *z0 = z; z = (PyLongObject *) long_sub(z0, (PyLongObject *)_PyLong_GetOne()); Py_DECREF(z0); - if (z == NULL) + if (z == NULL) { return NULL; + } } } @@ -4596,8 +4602,9 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (wordshift == 0 && IS_MEDIUM_VALUE(a) && (a->ob_digit[0] & ~(PyLong_MASK >> remshift)) == 0) { stwodigits sval = medium_value(a) << remshift; - if (IS_SMALL_INT(sval)) + if (IS_SMALL_INT(sval)) { return get_small_int((sdigit)sval); + } z = _PyLong_New(Py_SIZE(a)); z->ob_digit[0] = (digit)(a->ob_digit[0] << remshift); return (PyObject *)z; From 7d2203f2777e1650c36c3c3a20856c7752a4ab9d Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Tue, 14 Dec 2021 00:00:07 +0800 Subject: [PATCH 06/16] Fix: Bypass undefined shift operator behavior --- Objects/longobject.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 1b00f1f36c9ace..b496aa84e32daf 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4538,7 +4538,7 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (Py_SIZE(a) < 0) { Py_SET_SIZE(z, -newsize); - omitmark = a->ob_digit[wordshift] & ((1 << remshift) - 1); + omitmark = a->ob_digit[wordshift] & (((digit)1 << remshift) - 1); for (j = 0; omitmark == 0 && j < wordshift; j++) { omitmark |= a->ob_digit[j]; } @@ -4601,7 +4601,8 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (wordshift == 0 && IS_MEDIUM_VALUE(a) && (a->ob_digit[0] & ~(PyLong_MASK >> remshift)) == 0) { - stwodigits sval = medium_value(a) << remshift; + stwodigits sval = (stwodigits)((twodigits)medium_value(a) << remshift); + // bypass undefined shift operator behavior if (IS_SMALL_INT(sval)) { return get_small_int((sdigit)sval); } From 932ad87e7c431cbeae649da01cc0267d41200c8c Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Wed, 22 Dec 2021 23:33:44 +0800 Subject: [PATCH 07/16] Revert "Let PyLong_New accept a negtive size." This reverts commit dcd27965954068aaa79b0a84bfae807bea5f1d58. --- Objects/longobject.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index b496aa84e32daf..b7752317fda775 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -116,15 +116,14 @@ PyLongObject * _PyLong_New(Py_ssize_t size) { PyLongObject *result; - Py_ssize_t size_abs = Py_ABS(size); - if (size_abs > (Py_ssize_t)MAX_LONG_DIGITS) { + if (size > (Py_ssize_t)MAX_LONG_DIGITS) { PyErr_SetString(PyExc_OverflowError, "too many digits in integer"); return NULL; } /* Fast operations for single digit integers (including zero) * assume that there is always at least one digit present. */ - Py_ssize_t ndigits = size_abs ? size_abs : 1; + Py_ssize_t ndigits = size ? size : 1; /* Number of bytes needed is: offsetof(PyLongObject, ob_digit) + sizeof(digit)*size. Previous incarnations of this code used sizeof(PyVarObject) instead of the offsetof, but this risks being @@ -4509,7 +4508,8 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (IS_SMALL_INT(sval)) { return get_small_int((sdigit)sval); } - z = _PyLong_New(Py_SIZE(a)); + z = _PyLong_New(Py_ABS(Py_SIZE(a))); + Py_SET_SIZE(z, Py_SIZE(a)); z->ob_digit[0] = (digit)(a->ob_digit[0] >> remshift); if (Py_SIZE(a) < 0 && (a->ob_digit[0] & ~(PyLong_MASK << remshift))) { z->ob_digit[0] += 1; @@ -4606,7 +4606,8 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (IS_SMALL_INT(sval)) { return get_small_int((sdigit)sval); } - z = _PyLong_New(Py_SIZE(a)); + z = _PyLong_New(Py_ABS(Py_SIZE(a))); + Py_SET_SIZE(z, Py_SIZE(a)); z->ob_digit[0] = (digit)(a->ob_digit[0] << remshift); return (PyObject *)z; } From 596b8cfba788ceb2614348d93beb0e9084bc8684 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Wed, 22 Dec 2021 23:38:03 +0800 Subject: [PATCH 08/16] Revert: Remove additional inline keywords. --- Objects/longobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index b7752317fda775..44d82266cff229 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -47,7 +47,7 @@ static inline int is_medium_int(stwodigits x) return x_plus_mask < ((twodigits)PyLong_MASK) + PyLong_BASE; } -static inline PyObject * +static PyObject * get_small_int(sdigit ival) { assert(IS_SMALL_INT(ival)); @@ -56,7 +56,7 @@ get_small_int(sdigit ival) return v; } -static inline PyLongObject * +static PyLongObject * maybe_small_long(PyLongObject *v) { if (v && IS_MEDIUM_VALUE(v)) { From 6545e9a83ca95ad27b003a5ad4d789ae2bd3ff20 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Thu, 23 Dec 2021 11:45:16 +0800 Subject: [PATCH 09/16] Refactor: Simpilify code by using _PyLong_FromSTwoDigit --- Objects/longobject.c | 33 +++++++++------------------------ 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 44d82266cff229..124a8a358dd50b 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4495,28 +4495,19 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) Py_ssize_t newsize, hishift, i, j; digit lomask, himask, omitmark; - /* for a positive integrate x - -x >> m = -(x >> m) when the dropped bits are all zeros, - = -(x >> m) -1 otherwise. */ - if (IS_MEDIUM_VALUE(a)) { - stwodigits sval; + stwodigits x; if (wordshift > 0) { return get_small_int(-(Py_SIZE(a) < 0)); } - sval = Py_ARITHMETIC_RIGHT_SHIFT(stwodigits, medium_value(a), remshift); - if (IS_SMALL_INT(sval)) { - return get_small_int((sdigit)sval); - } - z = _PyLong_New(Py_ABS(Py_SIZE(a))); - Py_SET_SIZE(z, Py_SIZE(a)); - z->ob_digit[0] = (digit)(a->ob_digit[0] >> remshift); - if (Py_SIZE(a) < 0 && (a->ob_digit[0] & ~(PyLong_MASK << remshift))) { - z->ob_digit[0] += 1; - } - return (PyObject *)z; + x = Py_ARITHMETIC_RIGHT_SHIFT(stwodigits, medium_value(a), remshift); + return _PyLong_FromSTwoDigits(x); } + /* for a positive integrate x + -x >> m = -(x >> m) when the dropped bits are all zeros, + = -(x >> m) -1 otherwise. */ + newsize = Py_ABS(Py_SIZE(a)) - wordshift; if (newsize <= 0) { return PyLong_FromLong(0); @@ -4601,15 +4592,9 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (wordshift == 0 && IS_MEDIUM_VALUE(a) && (a->ob_digit[0] & ~(PyLong_MASK >> remshift)) == 0) { - stwodigits sval = (stwodigits)((twodigits)medium_value(a) << remshift); // bypass undefined shift operator behavior - if (IS_SMALL_INT(sval)) { - return get_small_int((sdigit)sval); - } - z = _PyLong_New(Py_ABS(Py_SIZE(a))); - Py_SET_SIZE(z, Py_SIZE(a)); - z->ob_digit[0] = (digit)(a->ob_digit[0] << remshift); - return (PyObject *)z; + stwodigits x = (stwodigits)((twodigits)medium_value(a) << remshift); + return _PyLong_FromSTwoDigits(x); } /* This version due to Tim Peters */ From 7f80cd924c96046f61077b4de68894c4fb7f185a Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Thu, 23 Dec 2021 12:03:13 +0800 Subject: [PATCH 10/16] Fix: Wrong rshift result. --- Objects/longobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 124a8a358dd50b..7404d136611172 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4510,7 +4510,7 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) newsize = Py_ABS(Py_SIZE(a)) - wordshift; if (newsize <= 0) { - return PyLong_FromLong(0); + return get_small_int(-(Py_SIZE(a) < 0)); } z = _PyLong_New(newsize); if (z == NULL) { From b24c7dcc7da47c69519a693401c9f66af742a030 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Thu, 23 Dec 2021 12:03:40 +0800 Subject: [PATCH 11/16] Test: More testcases. --- Lib/test/test_long.py | 63 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index e5c4d7fc4220f8..3c8e9e22e17a19 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -946,8 +946,13 @@ def test_huge_lshift(self, size): self.assertEqual(1 << (sys.maxsize + 1000), 1 << 1000 << sys.maxsize) def test_huge_rshift(self): - self.assertEqual(42 >> (1 << 1000), 0) - self.assertEqual((-42) >> (1 << 1000), -1) + huge_shift = 1 << 1000 + self.assertEqual(42 >> huge_shift, 0) + self.assertEqual((-42) >> huge_shift, -1) + self.assertEqual(1123 >> huge_shift, 0) + self.assertEqual((-1123) >> huge_shift, -1) + self.assertEqual(2**128 >> huge_shift, 0) + self.assertEqual(-2**128 >> huge_shift, -1) @support.cpython_only @support.bigmemtest(sys.maxsize + 500, memuse=2/15, dry_run=False) @@ -956,6 +961,60 @@ def test_huge_rshift_of_huge(self, size): self.assertEqual(huge >> (sys.maxsize + 1), (1 << 499) + 5) self.assertEqual(huge >> (sys.maxsize + 1000), 0) + def test_small_rshift(self): + self.assertEqual(42 >> 1, 21) + self.assertEqual((-42) >> 1, -21) + self.assertEqual(43 >> 1, 21) + self.assertEqual((-43) >> 1, -22) + + self.assertEqual(1122 >> 1, 561) + self.assertEqual((-1122) >> 1, -561) + self.assertEqual(1123 >> 1, 561) + self.assertEqual((-1123) >> 1, -562) + + self.assertEqual(2**128 >> 1, 2**127) + self.assertEqual(-2**128 >> 1, -2**127) + self.assertEqual((2**128 + 1) >> 1, 2**127) + self.assertEqual(-(2**128 + 1) >> 1, -2**127 - 1) + + def test_medium_rshift(self): + self.assertEqual(42 >> 9, 0) + self.assertEqual((-42) >> 9, -1) + self.assertEqual(1122 >> 9, 2) + self.assertEqual((-1122) >> 9, -3) + self.assertEqual(2**128 >> 9, 2**119) + self.assertEqual(-2**128 >> 9, -2**119) + + def test_big_rshift(self): + self.assertEqual(42 >> 32, 0) + self.assertEqual((-42) >> 32, -1) + self.assertEqual(1122 >> 32, 0) + self.assertEqual((-1122) >> 32, -1) + self.assertEqual(2**128 >> 32, 2**96) + self.assertEqual(-2**128 >> 32, -2**96) + + def test_small_lshift(self): + self.assertEqual(42 << 1, 84) + self.assertEqual((-42) << 1, -84) + self.assertEqual(561 << 1, 1122) + self.assertEqual((-561) << 1, -1122) + self.assertEqual(2**127 << 1, 2**128) + self.assertEqual(-2**127 << 1, -2**128) + + def test_medium_lshift(self): + self.assertEqual(42 << 9, 21504) + self.assertEqual((-42) << 9, -21504) + self.assertEqual(1122 << 9, 574464) + self.assertEqual((-1122) << 9, -574464) + + def test_big_lshift(self): + self.assertEqual(42 << 32, 42 * 2**32) + self.assertEqual((-42) << 32, -42 * 2**32) + self.assertEqual(1122 << 32, 1122 * 2**32) + self.assertEqual((-1122) << 32, -1122 * 2**32) + self.assertEqual(2**128 << 32, 2**160) + self.assertEqual(-2**128 << 32, -2**160) + @support.cpython_only def test_small_ints_in_huge_calculation(self): a = 2 ** 100 From c00a963661a9a48a9b29119b5f7eded570a37d77 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Fri, 24 Dec 2021 00:54:13 +0800 Subject: [PATCH 12/16] Refactor: Clean code. --- Objects/longobject.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 7404d136611172..35db0b65bc893b 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4496,11 +4496,11 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) digit lomask, himask, omitmark; if (IS_MEDIUM_VALUE(a)) { - stwodigits x; - if (wordshift > 0) { - return get_small_int(-(Py_SIZE(a) < 0)); - } - x = Py_ARITHMETIC_RIGHT_SHIFT(stwodigits, medium_value(a), remshift); + stwodigits m, x; + digit shift; + m = medium_value(a); + shift = wordshift == 0 ? remshift : PyLong_SHIFT; + x = m < 0 ? ~(~m >> shift) : m >> shift; return _PyLong_FromSTwoDigits(x); } @@ -4590,14 +4590,13 @@ long_lshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) Py_ssize_t oldsize, newsize, i, j; twodigits accum; - if (wordshift == 0 && IS_MEDIUM_VALUE(a) && - (a->ob_digit[0] & ~(PyLong_MASK >> remshift)) == 0) { + if (wordshift == 0 && IS_MEDIUM_VALUE(a)) { + stwodigits m = medium_value(a); // bypass undefined shift operator behavior - stwodigits x = (stwodigits)((twodigits)medium_value(a) << remshift); + stwodigits x = m < 0 ? -(-m << remshift) : m << remshift; return _PyLong_FromSTwoDigits(x); } - /* This version due to Tim Peters */ oldsize = Py_ABS(Py_SIZE(a)); newsize = oldsize + wordshift; if (remshift) From b7ad599894bdbf6aa3d78c7d37342f81de225f72 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Fri, 24 Dec 2021 00:59:09 +0800 Subject: [PATCH 13/16] Revert: Revert unrelated code. --- Objects/longobject.c | 66 +++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 35db0b65bc893b..c945252d4a433a 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4493,7 +4493,7 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) { PyLongObject *z = NULL; Py_ssize_t newsize, hishift, i, j; - digit lomask, himask, omitmark; + digit lomask, himask; if (IS_MEDIUM_VALUE(a)) { stwodigits m, x; @@ -4504,46 +4504,36 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) return _PyLong_FromSTwoDigits(x); } - /* for a positive integrate x - -x >> m = -(x >> m) when the dropped bits are all zeros, - = -(x >> m) -1 otherwise. */ - - newsize = Py_ABS(Py_SIZE(a)) - wordshift; - if (newsize <= 0) { - return get_small_int(-(Py_SIZE(a) < 0)); - } - z = _PyLong_New(newsize); - if (z == NULL) { - return NULL; - } - - hishift = PyLong_SHIFT - remshift; - lomask = ((digit)1 << hishift) - 1; - himask = PyLong_MASK ^ lomask; - for (i = 0, j = wordshift; i < newsize; i++, j++) { - z->ob_digit[i] = (a->ob_digit[j] >> remshift) & lomask; - if (i + 1 < newsize) { - z->ob_digit[i] |= (a->ob_digit[j + 1] << hishift) & himask; - } - } - if (Py_SIZE(a) < 0) { - Py_SET_SIZE(z, -newsize); - omitmark = a->ob_digit[wordshift] & (((digit)1 << remshift) - 1); - for (j = 0; omitmark == 0 && j < wordshift; j++) { - omitmark |= a->ob_digit[j]; - } - if (omitmark) { - PyLongObject *z0 = z; - z = (PyLongObject *) long_sub(z0, (PyLongObject *)_PyLong_GetOne()); - Py_DECREF(z0); - if (z == NULL) { - return NULL; - } + /* Right shifting negative numbers is harder */ + PyLongObject *a1, *a2; + a1 = (PyLongObject *)long_invert(a); + if (a1 == NULL) + return NULL; + a2 = (PyLongObject *)long_rshift1(a1, wordshift, remshift); + Py_DECREF(a1); + if (a2 == NULL) + return NULL; + z = (PyLongObject *)long_invert(a2); + Py_DECREF(a2); + } + else { + newsize = Py_SIZE(a) - wordshift; + if (newsize <= 0) + return PyLong_FromLong(0); + hishift = PyLong_SHIFT - remshift; + lomask = ((digit)1 << hishift) - 1; + himask = PyLong_MASK ^ lomask; + z = _PyLong_New(newsize); + if (z == NULL) + return NULL; + for (i = 0, j = wordshift; i < newsize; i++, j++) { + z->ob_digit[i] = (a->ob_digit[j] >> remshift) & lomask; + if (i + 1 < newsize) + z->ob_digit[i] |= (a->ob_digit[j + 1] << hishift) & himask; } + z = maybe_small_long(long_normalize(z)); } - - z = maybe_small_long(long_normalize(z)); return (PyObject *)z; } From c359521056e3078b16f73a24c24a90e63784dc08 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Fri, 24 Dec 2021 20:26:21 +0800 Subject: [PATCH 14/16] Docs: A couple more things. --- Misc/ACKS | 1 + .../2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst | 2 ++ Objects/longobject.c | 10 +++++----- 3 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst diff --git a/Misc/ACKS b/Misc/ACKS index 94a82a07506220..8baaf7304b603c 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1964,6 +1964,7 @@ Doug Wyatt Xiang Zhang Robert Xiao Florent Xicluna +Xinhang Xu Arnon Yaari Alakshendra Yadav Hirokazu Yamamoto diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst b/Misc/NEWS.d/next/Core and Builtins/2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst new file mode 100644 index 00000000000000..c660fea838e397 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst @@ -0,0 +1,2 @@ +Speed up shifting operation involving integrates less than +:c:macro:`PyLong_BASE`. diff --git a/Objects/longobject.c b/Objects/longobject.c index c945252d4a433a..3d78e7f51de96d 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -4507,14 +4507,14 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) if (Py_SIZE(a) < 0) { /* Right shifting negative numbers is harder */ PyLongObject *a1, *a2; - a1 = (PyLongObject *)long_invert(a); + a1 = (PyLongObject *) long_invert(a); if (a1 == NULL) return NULL; - a2 = (PyLongObject *)long_rshift1(a1, wordshift, remshift); + a2 = (PyLongObject *) long_rshift1(a1, wordshift, remshift); Py_DECREF(a1); if (a2 == NULL) return NULL; - z = (PyLongObject *)long_invert(a2); + z = (PyLongObject *) long_invert(a2); Py_DECREF(a2); } else { @@ -4529,8 +4529,8 @@ long_rshift1(PyLongObject *a, Py_ssize_t wordshift, digit remshift) return NULL; for (i = 0, j = wordshift; i < newsize; i++, j++) { z->ob_digit[i] = (a->ob_digit[j] >> remshift) & lomask; - if (i + 1 < newsize) - z->ob_digit[i] |= (a->ob_digit[j + 1] << hishift) & himask; + if (i+1 < newsize) + z->ob_digit[i] |= (a->ob_digit[j+1] << hishift) & himask; } z = maybe_small_long(long_normalize(z)); } From 241da4c272d4252a9cf71c3f29230152c50ce205 Mon Sep 17 00:00:00 2001 From: xuxinhang Date: Fri, 24 Dec 2021 21:20:10 +0800 Subject: [PATCH 15/16] Revert: medium_value --- Objects/longobject.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Objects/longobject.c b/Objects/longobject.c index 3d78e7f51de96d..d8faaedea34111 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -32,9 +32,7 @@ static inline stwodigits medium_value(PyLongObject *x) { assert(IS_MEDIUM_VALUE(x)); - Py_ssize_t s = Py_SIZE(x); - stwodigits d = x->ob_digit[0]; - return s == 1 ? d : s == -1 ? -d : 0; + return ((stwodigits)Py_SIZE(x)) * x->ob_digit[0]; } #define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS) From 9ed09aad05cb701c30995ed9516d0dfb241721ab Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 27 Dec 2021 09:43:07 +0000 Subject: [PATCH 16/16] Add attribution; fix typo --- .../2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst b/Misc/NEWS.d/next/Core and Builtins/2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst index c660fea838e397..124138806f17d0 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2021-12-24-20-21-45.bpo-46055.R0QMVQ.rst @@ -1,2 +1,2 @@ -Speed up shifting operation involving integrates less than -:c:macro:`PyLong_BASE`. +Speed up shifting operation involving integers less than +:c:macro:`PyLong_BASE`. Patch by Xinhang Xu.