Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Lib/test/test_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1626,8 +1626,12 @@ def test_sum(self):

self.assertEqual(sum(range(10), 1000), 1045)
self.assertEqual(sum(range(10), start=1000), 1045)
self.assertAlmostEqual(sum(range(10), 0.1), 45.1)
self.assertEqual(sum(range(10), 2**31-5), 2**31+40)
self.assertEqual(sum(range(10), 2**63-5), 2**63+40)
self.assertEqual(sum(range(-11, 13, 3)), -4)
self.assertEqual(sum(range(-11, -131, -2)), -4200)
self.assertEqual(sum(range(-100, -200, 13)), 0)

self.assertEqual(sum(i % 2 != 0 for i in range(10)), 5)
self.assertEqual(sum((i % 2 != 0 for i in range(10)), 2**31-3),
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ Hervé Coatanhay
Riccardo Coccioli
Nick Coghlan
Josh Cogliati
Marco Cognetta
Noam Cohen
Dave Cole
Terrence Cole
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reduce ``sum(range(...))`` from ``O(n)`` to ``O(1)`` time . Patch by Marco Cognetta.
59 changes: 59 additions & 0 deletions Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2489,13 +2489,72 @@ This function is intended specifically for use with numeric values and may
reject non-numeric types.
[clinic start generated code]*/

static PyObject* range_sum_fastpath(PyObject* module, PyObject *range)
{
PyObject* length = builtin_len(module, range);

if (PyObject_RichCompareBool(length, PyLong_FromLong(0), Py_EQ)) {
Py_DECREF(length);
return PyLong_FromLong(0);
}

// compute start * length + step * length * (length - 1) // 2
// [ a ]
// [ b ]
// [ c ]
// [ d ]
// [ e ]
// [ result ]

PyObject* start = PyObject_GetAttrString(range, "start");
PyObject* step = PyObject_GetAttrString(range, "step");

PyObject* one = PyLong_FromLong(1);
PyObject* a = PyNumber_Subtract(length, one);

PyObject* b = PyNumber_Multiply(a, length);

PyObject* two = PyLong_FromLong(2);
PyObject* c = PyNumber_FloorDivide(b, two);

PyObject* d = PyNumber_Multiply(step, c);

PyObject* e = PyNumber_Multiply(length, start);

PyObject* result = PyNumber_Add(d, e);
Comment on lines +2509 to +2524
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these calls can return NULL. You should prevent these situations.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, do you mean adding something like:

if (one == NULL) {
    Py_DECREF(one);
    return NULL;
}

after every one of these? I would have to do it after

        PyObject* rangesum =  range_sum_fastpath(module, iterable);
        result = PyNumber_Add(result, rangesum);
        Py_DECREF(rangesum);
        return result;

as well then, I think.


Py_DECREF(length);
Py_DECREF(start);
Py_DECREF(step);

Py_DECREF(one);
Py_DECREF(a);
Py_DECREF(b);
Py_DECREF(two);
Py_DECREF(c);
Py_DECREF(d);
Py_DECREF(e);

return result;
}

static PyObject *
builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
/*[clinic end generated code: output=df758cec7d1d302f input=162b50765250d222]*/
{
PyObject *result = start;
PyObject *temp, *item, *iter;

if (PyRange_Check(iterable)) {
Copy link
Member

@Eclips4 Eclips4 Aug 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

builtin_sum_impl written with the usage of Argument Clinic. So, you should run AC on this file to re-generate checksum's (you can see this at the beggining of this function).
More details about the Argument Clinic you can read here:
https://docs.python.org/3.13/howto/clinic.html

if (result == NULL) {
result = PyLong_FromLong(0);
}
PyObject* rangesum = range_sum_fastpath(module, iterable);
result = PyNumber_Add(result, rangesum);
Py_DECREF(rangesum);
return result;
}

iter = PyObject_GetIter(iterable);
if (iter == NULL)
return NULL;
Expand Down