Skip to content

Commit 3bd6a58

Browse files
committed
gh-118218: Reuse return tuple in itertools.pairwise
With this change: ``` b1(1) min=152.8us mean=155.5us ± 3.8us (25 repeats, 1000 loops) b2(1) min=149.0us mean=159.1us ± 8.8us (25 repeats, 1000 loops) b3(1) min=232.6us mean=242.5us ± 11.7us (25 repeats, 1000 loops) b1(10) min=279.2us mean=296.9us ± 16.6us (25 repeats, 1000 loops) b2(10) min=249.5us mean=259.2us ± 12.2us (25 repeats, 1000 loops) b3(10) min=386.6us mean=398.8us ± 10.1us (25 repeats, 1000 loops) b1(1000) min=20.3ms mean=20.7ms ± 0.5ms (25 repeats, 1000 loops) b2(1000) min=16.7ms mean=17.1ms ± 0.2ms (25 repeats, 1000 loops) b3(1000) min=26.0ms mean=26.2ms ± 0.3ms (25 repeats, 1000 loops) ``` Without this change: ``` b1(1) min=142.2us mean=143.0us ± 0.9us (25 repeats, 1000 loops) b2(1) min=142.7us mean=143.3us ± 1.0us (25 repeats, 1000 loops) b3(1) min=219.8us mean=227.2us ± 4.4us (25 repeats, 1000 loops) b1(10) min=314.2us mean=323.8us ± 4.1us (25 repeats, 1000 loops) b2(10) min=335.4us mean=341.8us ± 5.1us (25 repeats, 1000 loops) b3(10) min=362.0us mean=386.2us ± 14.9us (25 repeats, 1000 loops) b1(1000) min=26.5ms mean=27.3ms ± 0.3ms (25 repeats, 1000 loops) b2(1000) min=29.8ms mean=30.2ms ± 0.2ms (25 repeats, 1000 loops) b3(1000) min=26.0ms mean=26.5ms ± 0.4ms (25 repeats, 1000 loops) ```
1 parent 8e86579 commit 3bd6a58

File tree

1 file changed

+32
-2
lines changed

1 file changed

+32
-2
lines changed

Modules/itertoolsmodule.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ typedef struct {
270270
PyObject_HEAD
271271
PyObject *it;
272272
PyObject *old;
273+
PyObject* result;
273274
} pairwiseobject;
274275

275276
/*[clinic input]
@@ -301,6 +302,11 @@ pairwise_new_impl(PyTypeObject *type, PyObject *iterable)
301302
}
302303
po->it = it;
303304
po->old = NULL;
305+
po->result = PyTuple_Pack(2, Py_None, Py_None);
306+
if (po->result == NULL) {
307+
Py_DECREF(po);
308+
return NULL;
309+
}
304310
return (PyObject *)po;
305311
}
306312

@@ -311,6 +317,7 @@ pairwise_dealloc(pairwiseobject *po)
311317
PyObject_GC_UnTrack(po);
312318
Py_XDECREF(po->it);
313319
Py_XDECREF(po->old);
320+
Py_XDECREF(po->result);
314321
tp->tp_free(po);
315322
Py_DECREF(tp);
316323
}
@@ -321,6 +328,7 @@ pairwise_traverse(pairwiseobject *po, visitproc visit, void *arg)
321328
Py_VISIT(Py_TYPE(po));
322329
Py_VISIT(po->it);
323330
Py_VISIT(po->old);
331+
Py_VISIT(po->result);
324332
return 0;
325333
}
326334

@@ -355,8 +363,30 @@ pairwise_next(pairwiseobject *po)
355363
Py_DECREF(old);
356364
return NULL;
357365
}
358-
/* Future optimization: Reuse the result tuple as we do in enumerate() */
359-
result = PyTuple_Pack(2, old, new);
366+
367+
result = po->result;
368+
if (Py_REFCNT(result) == 1) {
369+
Py_INCREF(result);
370+
PyObject *last_old = PyTuple_GET_ITEM(result, 0);
371+
PyObject *last_new = PyTuple_GET_ITEM(result, 1);
372+
PyTuple_SET_ITEM(result, 0, Py_NewRef(old));
373+
PyTuple_SET_ITEM(result, 1, Py_NewRef(new));
374+
Py_DECREF(last_old);
375+
Py_DECREF(last_new);
376+
// bpo-42536: The GC may have untracked this result tuple. Since we're
377+
// recycling it, make sure it's tracked again:
378+
if (!_PyObject_GC_IS_TRACKED(result)) {
379+
_PyObject_GC_TRACK(result);
380+
}
381+
}
382+
else {
383+
result = PyTuple_New(2);
384+
if (result != NULL) {
385+
PyTuple_SET_ITEM(result, 0, Py_NewRef(old));
386+
PyTuple_SET_ITEM(result, 1, Py_NewRef(new));
387+
}
388+
}
389+
360390
Py_XSETREF(po->old, new);
361391
Py_DECREF(old);
362392
return result;

0 commit comments

Comments
 (0)