Skip to content

Commit 332d608

Browse files
Tune the merge threshold and stack size
Merge a new value with stacked values while it has at least 3/4 (instead of 1/2) as many digits: this matches the balanced pairing of equal-sized arguments while keeping the size adaptivity, and removes the slowdown for many equal-sized arguments. Cache the digit counts of stacked values in a parallel array. Since entry sizes now grow at least 4/3 times (instead of 2 times) per entry, enlarge the stack from 8*sizeof(Py_ssize_t) to 24*sizeof(Py_ssize_t) entries, which covers the new depth bound of log(PY_SSIZE_T_MAX)/log(4/3) + 1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 1ee08c8 commit 332d608

1 file changed

Lines changed: 12 additions & 11 deletions

File tree

Modules/mathintegermodule.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -118,35 +118,36 @@ math_integer_lcm_impl(PyObject *module, PyObject * const *args,
118118
return PyLong_FromLong(1);
119119
}
120120
/* Combine intermediate results in size-balanced order: a new value
121-
is merged with stacked values while it has at least half as many
122-
digits, so every stack entry has more than twice as many digits
123-
as the one above it. Small arguments are thus combined with each
124-
other before touching a much larger partial result. The doubling
125-
invariant bounds the stack depth by the bit width of the maximal
126-
digit count, so the stack cannot overflow. */
121+
is merged with stacked values while it has at least 3/4 as many
122+
digits, so the sizes of stack entries grow at least 4/3 times
123+
per entry. Small arguments are thus combined with each other
124+
before touching a much larger partial result. The stack depth
125+
is bounded by log(PY_SSIZE_T_MAX)/log(4/3) plus one possible
126+
zero entry on top: 153 on 64-bit and 76 on 32-bit platforms. */
127+
PyObject *stack[24 * sizeof(Py_ssize_t)];
128+
Py_ssize_t sizes[24 * sizeof(Py_ssize_t)];
127129
PyObject *res;
128-
PyObject *stack[8 * sizeof(Py_ssize_t)];
129130
int top = 0;
130131
for (Py_ssize_t i = 0; ; i++) {
131132
res = PyNumber_Index(args[i]);
132133
if (res == NULL) {
133134
goto error;
134135
}
135-
while (top > 0
136-
&& (_PyLong_DigitCount((PyLongObject *)res)
137-
>= _PyLong_DigitCount((PyLongObject *)stack[top-1]) / 2))
138-
{
136+
Py_ssize_t dres = _PyLong_DigitCount((PyLongObject *)res);
137+
while (top > 0 && dres >= sizes[top-1] - sizes[top-1] / 4) {
139138
top--;
140139
Py_SETREF(res, long_lcm(res, stack[top]));
141140
Py_DECREF(stack[top]);
142141
if (res == NULL) {
143142
goto error;
144143
}
144+
dres = _PyLong_DigitCount((PyLongObject *)res);
145145
}
146146
if (i + 1 >= args_length) {
147147
break;
148148
}
149149
assert(top < (int)Py_ARRAY_LENGTH(stack));
150+
sizes[top] = dres;
150151
stack[top++] = res;
151152
}
152153
while (top > 0) {

0 commit comments

Comments
 (0)