Skip to content

Commit 5cbd833

Browse files
committed
Issue python#24221: Small optimizations for heapq.
Replaces the PyList_GET_ITEM and PyList_SET_ITEM macros with normal array accesses. Replace the siftup unpredicatable branch with arithmetic. Replace the rc == -1 tests with rc < 0. Gives nicer looking assembly with both Clang and GCC-4.9. Also gives a small performance both for both.
1 parent 35e24a5 commit 5cbd833

File tree

2 files changed

+44
-37
lines changed

2 files changed

+44
-37
lines changed

Include/listobject.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out);
7272
#define PyList_GET_ITEM(op, i) (((PyListObject *)(op))->ob_item[i])
7373
#define PyList_SET_ITEM(op, i, v) (((PyListObject *)(op))->ob_item[i] = (v))
7474
#define PyList_GET_SIZE(op) Py_SIZE(op)
75+
#define _PyList_ITEMS(op) (((PyListObject *)(op))->ob_item)
7576
#endif
7677

7778
#ifdef __cplusplus

Modules/_heapqmodule.c

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ annotated by François Pinard, and converted to C by Raymond Hettinger.
1111
static int
1212
siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
1313
{
14-
PyObject *newitem, *parent;
14+
PyObject *newitem, *parent, **arr;
1515
Py_ssize_t parentpos, size;
1616
int cmp;
1717

@@ -24,12 +24,13 @@ siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
2424

2525
/* Follow the path to the root, moving parents down until finding
2626
a place newitem fits. */
27-
newitem = PyList_GET_ITEM(heap, pos);
27+
arr = _PyList_ITEMS(heap);
28+
newitem = arr[pos];
2829
while (pos > startpos) {
2930
parentpos = (pos - 1) >> 1;
30-
parent = PyList_GET_ITEM(heap, parentpos);
31+
parent = arr[parentpos];
3132
cmp = PyObject_RichCompareBool(newitem, parent, Py_LT);
32-
if (cmp == -1)
33+
if (cmp < 0)
3334
return -1;
3435
if (size != PyList_GET_SIZE(heap)) {
3536
PyErr_SetString(PyExc_RuntimeError,
@@ -38,10 +39,11 @@ siftdown(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
3839
}
3940
if (cmp == 0)
4041
break;
41-
parent = PyList_GET_ITEM(heap, parentpos);
42-
newitem = PyList_GET_ITEM(heap, pos);
43-
PyList_SET_ITEM(heap, parentpos, newitem);
44-
PyList_SET_ITEM(heap, pos, parent);
42+
arr = _PyList_ITEMS(heap);
43+
parent = arr[parentpos];
44+
newitem = arr[pos];
45+
arr[parentpos] = newitem;
46+
arr[pos] = parent;
4547
pos = parentpos;
4648
}
4749
return 0;
@@ -51,7 +53,7 @@ static int
5153
siftup(PyListObject *heap, Py_ssize_t pos)
5254
{
5355
Py_ssize_t startpos, endpos, childpos, limit;
54-
PyObject *tmp1, *tmp2;
56+
PyObject *tmp1, *tmp2, **arr;
5557
int cmp;
5658

5759
assert(PyList_Check(heap));
@@ -63,30 +65,31 @@ siftup(PyListObject *heap, Py_ssize_t pos)
6365
}
6466

6567
/* Bubble up the smaller child until hitting a leaf. */
68+
arr = _PyList_ITEMS(heap);
6669
limit = endpos / 2; /* smallest pos that has no child */
6770
while (pos < limit) {
6871
/* Set childpos to index of smaller child. */
6972
childpos = 2*pos + 1; /* leftmost child position */
7073
if (childpos + 1 < endpos) {
7174
cmp = PyObject_RichCompareBool(
72-
PyList_GET_ITEM(heap, childpos),
73-
PyList_GET_ITEM(heap, childpos + 1),
75+
arr[childpos],
76+
arr[childpos + 1],
7477
Py_LT);
75-
if (cmp == -1)
78+
if (cmp < 0)
7679
return -1;
77-
if (cmp == 0)
78-
childpos++; /* rightmost child is smallest */
80+
childpos += ((unsigned)cmp ^ 1); /* increment when cmp==0 */
7981
if (endpos != PyList_GET_SIZE(heap)) {
8082
PyErr_SetString(PyExc_RuntimeError,
8183
"list changed size during iteration");
8284
return -1;
8385
}
8486
}
8587
/* Move the smaller child up. */
86-
tmp1 = PyList_GET_ITEM(heap, childpos);
87-
tmp2 = PyList_GET_ITEM(heap, pos);
88-
PyList_SET_ITEM(heap, childpos, tmp2);
89-
PyList_SET_ITEM(heap, pos, tmp1);
88+
arr = _PyList_ITEMS(heap);
89+
tmp1 = arr[childpos];
90+
tmp2 = arr[pos];
91+
arr[childpos] = tmp2;
92+
arr[pos] = tmp1;
9093
pos = childpos;
9194
}
9295
/* Bubble it up to its final resting place (by sifting its parents down). */
@@ -227,7 +230,7 @@ heappushpop(PyObject *self, PyObject *args)
227230
}
228231

229232
cmp = PyObject_RichCompareBool(PyList_GET_ITEM(heap, 0), item, Py_LT);
230-
if (cmp == -1)
233+
if (cmp < 0)
231234
return NULL;
232235
if (cmp == 0) {
233236
Py_INCREF(item);
@@ -362,7 +365,7 @@ PyDoc_STRVAR(heapify_doc,
362365
static int
363366
siftdown_max(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
364367
{
365-
PyObject *newitem, *parent;
368+
PyObject *newitem, *parent, **arr;
366369
Py_ssize_t parentpos, size;
367370
int cmp;
368371

@@ -375,12 +378,13 @@ siftdown_max(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
375378

376379
/* Follow the path to the root, moving parents down until finding
377380
a place newitem fits. */
378-
newitem = PyList_GET_ITEM(heap, pos);
381+
arr = _PyList_ITEMS(heap);
382+
newitem = arr[pos];
379383
while (pos > startpos) {
380384
parentpos = (pos - 1) >> 1;
381-
parent = PyList_GET_ITEM(heap, parentpos);
385+
parent = arr[parentpos];
382386
cmp = PyObject_RichCompareBool(parent, newitem, Py_LT);
383-
if (cmp == -1)
387+
if (cmp < 0)
384388
return -1;
385389
if (size != PyList_GET_SIZE(heap)) {
386390
PyErr_SetString(PyExc_RuntimeError,
@@ -389,10 +393,11 @@ siftdown_max(PyListObject *heap, Py_ssize_t startpos, Py_ssize_t pos)
389393
}
390394
if (cmp == 0)
391395
break;
392-
parent = PyList_GET_ITEM(heap, parentpos);
393-
newitem = PyList_GET_ITEM(heap, pos);
394-
PyList_SET_ITEM(heap, parentpos, newitem);
395-
PyList_SET_ITEM(heap, pos, parent);
396+
arr = _PyList_ITEMS(heap);
397+
parent = arr[parentpos];
398+
newitem = arr[pos];
399+
arr[parentpos] = newitem;
400+
arr[pos] = parent;
396401
pos = parentpos;
397402
}
398403
return 0;
@@ -402,7 +407,7 @@ static int
402407
siftup_max(PyListObject *heap, Py_ssize_t pos)
403408
{
404409
Py_ssize_t startpos, endpos, childpos, limit;
405-
PyObject *tmp1, *tmp2;
410+
PyObject *tmp1, *tmp2, **arr;
406411
int cmp;
407412

408413
assert(PyList_Check(heap));
@@ -414,30 +419,31 @@ siftup_max(PyListObject *heap, Py_ssize_t pos)
414419
}
415420

416421
/* Bubble up the smaller child until hitting a leaf. */
422+
arr = _PyList_ITEMS(heap);
417423
limit = endpos / 2; /* smallest pos that has no child */
418424
while (pos < limit) {
419425
/* Set childpos to index of smaller child. */
420426
childpos = 2*pos + 1; /* leftmost child position */
421427
if (childpos + 1 < endpos) {
422428
cmp = PyObject_RichCompareBool(
423-
PyList_GET_ITEM(heap, childpos + 1),
424-
PyList_GET_ITEM(heap, childpos),
429+
arr[childpos + 1],
430+
arr[childpos],
425431
Py_LT);
426-
if (cmp == -1)
432+
if (cmp < 0)
427433
return -1;
428-
if (cmp == 0)
429-
childpos++; /* rightmost child is smallest */
434+
childpos += ((unsigned)cmp ^ 1); /* increment when cmp==0 */
430435
if (endpos != PyList_GET_SIZE(heap)) {
431436
PyErr_SetString(PyExc_RuntimeError,
432437
"list changed size during iteration");
433438
return -1;
434439
}
435440
}
436441
/* Move the smaller child up. */
437-
tmp1 = PyList_GET_ITEM(heap, childpos);
438-
tmp2 = PyList_GET_ITEM(heap, pos);
439-
PyList_SET_ITEM(heap, childpos, tmp2);
440-
PyList_SET_ITEM(heap, pos, tmp1);
442+
arr = _PyList_ITEMS(heap);
443+
tmp1 = arr[childpos];
444+
tmp2 = arr[pos];
445+
arr[childpos] = tmp2;
446+
arr[pos] = tmp1;
441447
pos = childpos;
442448
}
443449
/* Bubble it up to its final resting place (by sifting its parents down). */

0 commit comments

Comments
 (0)