[Python-checkins] A (very) slight speed improvement for iterating over bytes (#21705)

Guido van Rossum webhook-mailer at python.org
Mon Aug 3 12:04:21 EDT 2020


https://github.com/python/cpython/commit/488512bf4953d856fcb049a05060a450af52fcdc
commit: 488512bf4953d856fcb049a05060a450af52fcdc
branch: master
author: Guido van Rossum <guido at python.org>
committer: GitHub <noreply at github.com>
date: 2020-08-03T09:04:13-07:00
summary:

A (very) slight speed improvement for iterating over bytes (#21705)

My mentee @xvxvxvxvxv noticed that iterating over array.array is
slightly faster than iterating over bytes.  Looking at the source I
observed that arrayiter_next() calls `getitem(ao, it->index++)` wheras
striter_next() uses the idiom (paraphrased)

    item = PyLong_FromLong(seq->ob_sval[it->it_index]);
    if (item != NULL)
        ++it->it_next;
    return item;

I'm not 100% sure but I think that the second version has fewer
opportunity for the CPU to overlap the `index++` operation with the
rest of the code (which in both cases involves a call).  So here I am
optimistically incrementing the index -- if the PyLong_FromLong() call
fails, this will leave the iterator pointing at the next byte, but
honestly I doubt that anyone would seriously consider resuming use of
the iterator after that kind of failure (it would have to be a
MemoryError).  And the author of arrayiter_next() made the same
consideration (or never ever gave it a thought :-).

With this, a loop like

    for _ in b: pass

is now slightly *faster* than the same thing over an equivalent array,
rather than slightly *slower* (in both cases a few percent).

files:
M Objects/bytesobject.c

diff --git a/Objects/bytesobject.c b/Objects/bytesobject.c
index 3a922d32b16e4..836a736037ba4 100644
--- a/Objects/bytesobject.c
+++ b/Objects/bytesobject.c
@@ -3139,7 +3139,6 @@ static PyObject *
 striter_next(striterobject *it)
 {
     PyBytesObject *seq;
-    PyObject *item;
 
     assert(it != NULL);
     seq = it->it_seq;
@@ -3148,11 +3147,8 @@ striter_next(striterobject *it)
     assert(PyBytes_Check(seq));
 
     if (it->it_index < PyBytes_GET_SIZE(seq)) {
-        item = PyLong_FromLong(
-            (unsigned char)seq->ob_sval[it->it_index]);
-        if (item != NULL)
-            ++it->it_index;
-        return item;
+        return PyLong_FromLong(
+            (unsigned char)seq->ob_sval[it->it_index++]);
     }
 
     it->it_seq = NULL;



More information about the Python-checkins mailing list