This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author jcon
Recipients benjamin.peterson, daniel.urban, jcon, pitrou, stutzbach
Date 2011-05-08.23:13:04
SpamBayes Score 1.110223e-16
Marked as misclassified No
Message-id <1304896386.11.0.128008274277.issue9971@psf.upfronthosting.co.za>
In-reply-to
Content
I experimented with a bunch of different options.

All benchmarks performed with:

$ for i in 1 4 128 256 1024 2048 4069 8192; do 
echo -n "buffer_size=${i} "; 
./python -m timeit -s "f=open('LICENSE','rb');b=bytearray(${i})" \
"f.seek(0)" "while f.readinto(b): pass";
done

with io.DEFAULT_BUFFER_SIZE = 8192

--------------------------------------------------------------------------------
Before patch

buffer_size=1 100 loops, best of 3: 10.4 msec per loop
buffer_size=4 100 loops, best of 3: 2.67 msec per loop
buffer_size=128 10000 loops, best of 3: 102 usec per loop
buffer_size=256 10000 loops, best of 3: 54.9 usec per loop
buffer_size=1024 10000 loops, best of 3: 26.9 usec per loop
buffer_size=2048 10000 loops, best of 3: 20.3 usec per loop
buffer_size=4069 100000 loops, best of 3: 16.3 usec per loop
buffer_size=8192 100000 loops, best of 3: 11.1 usec per loop

--------------------------------------------------------------------------------
Always read into caller's buffer

buffer_size=1 100 loops, best of 3: 14 msec per loop
buffer_size=4 100 loops, best of 3: 4.02 msec per loop
buffer_size=128 10000 loops, best of 3: 114 usec per loop
buffer_size=256 10000 loops, best of 3: 63.7 usec per loop
buffer_size=1024 100000 loops, best of 3: 19.4 usec per loop
buffer_size=2048 100000 loops, best of 3: 11.2 usec per loop
* buffer_size=4069 100000 loops, best of 3: 8.12 usec per loop
* buffer_size=8192 100000 loops, best of 3: 5.79 usec per loop

--------------------------------------------------------------------------------
Read into caller's buffer if java-like bound of internal buffer size is smaller

buffer_size=1 100 loops, best of 3: 5.01 msec per loop
buffer_size=4 1000 loops, best of 3: 1.27 msec per loop
buffer_size=128 10000 loops, best of 3: 46.9 usec per loop
buffer_size=256 10000 loops, best of 3: 29.5 usec per loop
buffer_size=1024 100000 loops, best of 3: 12.9 usec per loop
buffer_size=2048 100000 loops, best of 3: 10.8 usec per loop
buffer_size=4069 100000 loops, best of 3: 9.06 usec per loop
* buffer_size=8192 100000 loops, best of 3: 5.78 usec per loop

--------------------------------------------------------------------------------
Using bound = buffer_size / 2

buffer_size=1 100 loops, best of 3: 6.04 msec per loop
buffer_size=4 1000 loops, best of 3: 1.34 msec per loop
buffer_size=128 10000 loops, best of 3: 49.4 usec per loop
buffer_size=256 10000 loops, best of 3: 29.5 usec per loop
buffer_size=1024 100000 loops, best of 3: 13.2 usec per loop
buffer_size=2048 100000 loops, best of 3: 10.7 usec per loop
* buffer_size=4069 100000 loops, best of 3: 8.66 usec per loop
buffer_size=8192 100000 loops, best of 3: 6.1 usec per loop

--------------------------------------------------------------------------------
Using bound = buffer_size / 4

buffer_size=1 100 loops, best of 3: 5.45 msec per loop
buffer_size=4 1000 loops, best of 3: 1.34 msec per loop
buffer_size=128 10000 loops, best of 3: 49.6 usec per loop
buffer_size=256 10000 loops, best of 3: 28.8 usec per loop
buffer_size=1024 100000 loops, best of 3: 13.1 usec per loop
buffer_size=2048 100000 loops, best of 3: 12.8 usec per loop
buffer_size=4069 100000 loops, best of 3: 8.42 usec per loop
buffer_size=8192 100000 loops, best of 3: 5.93 usec per loop

--------------------------------------------------------------------------------
Always use internal buffer

* buffer_size=1 100 loops, best of 3: 4.53 msec per loop
* buffer_size=4 1000 loops, best of 3: 1.14 msec per loop
* buffer_size=128 10000 loops, best of 3: 45 usec per loop
* buffer_size=256 10000 loops, best of 3: 26.9 usec per loop
* buffer_size=1024 100000 loops, best of 3: 12.9 usec per loop
* buffer_size=2048 100000 loops, best of 3: 10.2 usec per loop
buffer_size=4069 100000 loops, best of 3: 9.15 usec per loop
buffer_size=8192 100000 loops, best of 3: 8.42 usec per loop

--------------------------------------------------------------------------------
Use read() instead

$ for i in 1 4 128 256 1024 2048 4069 8192; do
echo -n "size=${i} "; 
./python -m timeit -s "f=open('LICENSE','rb');" "f.seek(0)" \
"while f.read(${i}): pass";
done

* size=1 100 loops, best of 3: 2.56 msec per loop
* size=4 1000 loops, best of 3: 709 usec per loop
* size=128 10000 loops, best of 3: 29.7 usec per loop
* size=256 100000 loops, best of 3: 18.6 usec per loop
size=1024 100000 loops, best of 3: 13.3 usec per loop
size=2048 100000 loops, best of 3: 10.8 usec per loop
size=4069 100000 loops, best of 3: 10.1 usec per loop
size=8192 100000 loops, best of 3: 6.41 usec per loop

--------------------------------------------------------------------------------


Based on the above I think you are right about using the internal buffer
regardless (revision attached). You pay a price with larger buffer sizes but on
balance it seems to be a much better general purpose solution. The java-like 
solution is decent as well, it is only slightly slower for small reads and optimal
for larger buffer sizes. Personally, I would opt for the performance with larger
buffer sizes but I can only speculate as to what the general user would do. We
could always note the trade-off in the docs.

Interestingly enough it seems like using read() is a better idea
if you use a smaller size (how is that right?).

I attached the afformentioned revisions.
History
Date User Action Args
2011-05-08 23:13:07jconsetrecipients: + jcon, pitrou, benjamin.peterson, stutzbach, daniel.urban
2011-05-08 23:13:06jconsetmessageid: <1304896386.11.0.128008274277.issue9971@psf.upfronthosting.co.za>
2011-05-08 23:13:05jconlinkissue9971 messages
2011-05-08 23:13:05jconcreate