This is very similar to this question but there's a slight difference in my question.
I have two functionally identical functions as follows:
def count1(text):
c = 0
for i, j in zip(text, reversed(text)):
if i == j:
c += 1
return c
def count2(text):
c = 0
for i, j in zip(text, text[::-1]):
if i == j:
c += 1
return c
I would not expect count2() to perform as well as count1() on the grounds that there's no new list created - just an iterator.
I have determined empirically that this is true for short strings. However, as the length of the strings increase, the performance characteristics seem to change.
Here's the test rig:
from timeit import timeit
from random import choices
from string import ascii_lowercase
for k in range(2, 21, 2):
print(f'{k=}')
text = ''.join(choices(ascii_lowercase, k=k))
for func in count1, count2:
print(func.__name__, f'{timeit(lambda: func(text)):.4f}')
Now consider the output and note that count1() is faster than count2() for string lengths less than 12. As the string length increases count2() runs faster.
The differences are minimal and unlikely to ever be critical. It is however intriguing and I'd like to understand why this might be the case.
Output:
k=2
count1 0.2870
count2 0.3247
k=4
count1 0.3454
count2 0.3740
k=6
count1 0.4228
count2 0.4518
k=8
count1 0.4799
count2 0.4990
k=10
count1 0.5382
count2 0.5584
k=12
count1 0.6406
count2 0.6426
k=14
count1 0.6792
count2 0.6555
k=16
count1 0.7593
count2 0.7480
k=18
count1 0.8595
count2 0.8335
k=20
count1 0.8538
count2 0.8121
Notes:
macOS -> 13.3
Python -> 3.11.2
CPU -> 3GHz 10-Core Intel Xeon W
RAM -> 32GB
Strings don't implement __reversed__, so they go through the generic reversed implementation that just indexes the sequence with indexes from len(seq)-1 to 0.
On Python 3.11, "compact ASCII" strings have a specialized iterator type that uses a more efficient __next__ implementation taking advantage of the fact that all code points are in the ASCII range.
text[::-1] is a compact ASCII string, so iterating over text[::-1] uses the specialized ASCII iterator. On the other hand, iterating over reversed(text) has to go through the generic __getitem__ implementation with no special optimizations. Depending on system details like how fast your cache is, this may be enough for text[::-1] to get a small advantage in a microbenchmark like this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With