Skip to content

Commit 06d0a8c

Browse files
committed
gh-151289: specialize shrinking int subtraction
1 parent c22e9c9 commit 06d0a8c

14 files changed

Lines changed: 434 additions & 104 deletions

File tree

Include/internal/pycore_long.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ PyAPI_DATA(PyObject*) _PyLong_Lshift(PyObject *, int64_t);
116116
PyAPI_FUNC(_PyStackRef) _PyCompactLong_Add(PyLongObject *left, PyLongObject *right);
117117
PyAPI_FUNC(_PyStackRef) _PyCompactLong_Multiply(PyLongObject *left, PyLongObject *right);
118118
PyAPI_FUNC(_PyStackRef) _PyCompactLong_Subtract(PyLongObject *left, PyLongObject *right);
119+
PyAPI_FUNC(_PyStackRef) _PyLong_SubtractShrink(PyLongObject *left, PyLongObject *right);
119120

120121
// Export for 'binascii' shared extension.
121122
PyAPI_DATA(unsigned char) _PyLong_DigitValue[256];
@@ -346,6 +347,28 @@ _PyLong_CheckExactAndCompact(PyObject *op)
346347
return PyLong_CheckExact(op) && _PyLong_IsCompact((const PyLongObject *)op);
347348
}
348349

350+
static inline bool
351+
_PyLong_IsShrinkSubtractPair(const PyLongObject *left, const PyLongObject *right)
352+
{
353+
#if PyLong_SHIFT == 30
354+
if (_PyLong_IsCompact((PyLongObject *)left) ||
355+
_PyLong_IsCompact((PyLongObject *)right) ||
356+
!_PyLong_SameSign(left, right)) {
357+
return false;
358+
}
359+
Py_ssize_t left_size = _PyLong_DigitCount(left);
360+
if (left_size != _PyLong_DigitCount(right) ||
361+
left_size < 2 ||
362+
left_size > 3) {
363+
return false;
364+
}
365+
return left->long_value.ob_digit[left_size - 1] ==
366+
right->long_value.ob_digit[left_size - 1];
367+
#else
368+
return false;
369+
#endif
370+
}
371+
349372
#ifdef __cplusplus
350373
}
351374
#endif

Include/internal/pycore_opcode_metadata.h

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_ids.h

Lines changed: 12 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_uop_metadata.h

Lines changed: 46 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_capi/test_opt.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4455,6 +4455,22 @@ def testfunc(n):
44554455
self.assertIn("_POP_TOP_NOP", uops)
44564456
self.assertLessEqual(count_ops(ex, "_POP_TOP"), 2)
44574457

4458+
def test_int_subtract_shrink_specialization(self):
4459+
def testfunc(args):
4460+
a, b, n = args
4461+
res = 0
4462+
for _ in range(n):
4463+
res = a - b
4464+
return res
4465+
4466+
res, ex = self._run_with_optimizer(
4467+
testfunc, (10_000_000_001, 10_000_000_000, TIER2_THRESHOLD)
4468+
)
4469+
self.assertEqual(res, 1)
4470+
self.assertIsNotNone(ex)
4471+
uops = get_opnames(ex)
4472+
self.assertIn("_BINARY_OP_SUBTRACT_INT", uops)
4473+
44584474
def test_int_mul_op_refcount_elimination(self):
44594475
def testfunc(n):
44604476
c = 1

Lib/test/test_long.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,25 @@ def test_small_ints(self):
11401140
self.assertIs(i - i, 0)
11411141
self.assertIs(0 * i, 0)
11421142

1143+
@support.cpython_only
1144+
def test_int64_boundary_sub(self):
1145+
INT64_MAX = (1 << 63) - 1
1146+
INT64_MIN = -(1 << 63)
1147+
1148+
self.assertEqual(INT64_MIN - 0, INT64_MIN)
1149+
self.assertEqual(INT64_MIN - (-1), INT64_MIN + 1)
1150+
1151+
self.assertEqual(INT64_MAX - 0, INT64_MAX)
1152+
self.assertEqual(INT64_MAX - 1, INT64_MAX - 1)
1153+
1154+
self.assertEqual(INT64_MIN - 1, INT64_MIN - 1)
1155+
self.assertEqual(INT64_MAX - (-1), 1 << 63)
1156+
1157+
beyond_max = INT64_MAX + 2
1158+
beyond_min = INT64_MIN - 2
1159+
self.assertEqual(beyond_max - 1, INT64_MAX + 1)
1160+
self.assertEqual(beyond_min - (-1), INT64_MIN - 1)
1161+
11431162
def test_bit_length(self):
11441163
tiny = 1e-10
11451164
for x in range(-65000, 65000):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve performance of binary ``int`` subtraction for shrinking wide exact
2+
integers.

Modules/_testinternalcapi/test_cases.c.h

Lines changed: 13 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)