Author: Manuel Jacob <[email protected]>
Branch:
Changeset: r90517:2d9e1b4a2e61
Date: 2017-03-03 23:21 +0100
http://bitbucket.org/pypy/pypy/changeset/2d9e1b4a2e61/
Log: Add optimized "zero-copy" path for io.FileIO.readinto().
This path is taken if the passed buffer has a raw address and is
bigger than 64 bytes.
diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -504,14 +504,16 @@
exception_name=exception_name,
w_exception_class=w_exception_class)
-def exception_from_saved_errno(space, w_type):
- from rpython.rlib.rposix import get_saved_errno
-
- errno = get_saved_errno()
+def exception_from_errno(space, w_type, errno):
msg = os.strerror(errno)
w_error = space.call_function(w_type, space.newint(errno),
space.newtext(msg))
return OperationError(w_type, w_error)
+def exception_from_saved_errno(space, w_type):
+ from rpython.rlib.rposix import get_saved_errno
+ errno = get_saved_errno()
+ return exception_from_errno(space, w_type, errno)
+
def new_exception_class(space, name, w_bases=None, w_dict=None):
"""Create a new exception type.
@param name: the name of the type.
diff --git a/pypy/module/_io/interp_bufferedio.py
b/pypy/module/_io/interp_bufferedio.py
--- a/pypy/module/_io/interp_bufferedio.py
+++ b/pypy/module/_io/interp_bufferedio.py
@@ -4,7 +4,8 @@
from pypy.interpreter.typedef import (
TypeDef, GetSetProperty, generic_new_descr, interp_attrproperty_w)
from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault
-from rpython.rlib.rgc import nonmoving_raw_ptr_for_resizable_list
+from rpython.rlib.rgc import (
+ nonmoving_raw_ptr_for_resizable_list, resizable_list_supporting_raw_ptr)
from rpython.rlib.buffer import Buffer
from rpython.rlib.rstring import StringBuilder
from rpython.rlib.rarithmetic import r_longlong, intmask
@@ -162,7 +163,8 @@
raise oefmt(space.w_ValueError,
"buffer size must be strictly positive")
- self.buffer = ['\0'] * self.buffer_size
+ self.buffer = resizable_list_supporting_raw_ptr(['\0'] *
+ self.buffer_size)
self.lock = TryLock(space)
@@ -561,7 +563,7 @@
if n <= current_size:
return self._read_fast(n)
- result_buffer = ['\0'] * n
+ result_buffer = resizable_list_supporting_raw_ptr(['\0'] * n)
remaining = n
written = 0
if current_size:
diff --git a/pypy/module/_io/interp_fileio.py b/pypy/module/_io/interp_fileio.py
--- a/pypy/module/_io/interp_fileio.py
+++ b/pypy/module/_io/interp_fileio.py
@@ -1,9 +1,11 @@
from pypy.interpreter.typedef import TypeDef, interp_attrproperty,
GetSetProperty
from pypy.interpreter.gateway import interp2app, unwrap_spec
from pypy.interpreter.error import (
- OperationError, oefmt, wrap_oserror, wrap_oserror2)
+ OperationError, oefmt, wrap_oserror, wrap_oserror2, exception_from_errno)
from rpython.rlib.rarithmetic import r_longlong
+from rpython.rlib.rposix import get_saved_errno
from rpython.rlib.rstring import StringBuilder
+from rpython.rtyper.lltypesystem import lltype, rffi
from os import O_RDONLY, O_WRONLY, O_RDWR, O_CREAT, O_TRUNC
import sys, os, stat, errno
from pypy.module._io.interp_iobase import W_RawIOBase, convert_size
@@ -112,6 +114,15 @@
return currentsize + BIGCHUNK
return currentsize + SMALLCHUNK
+
+_WIN32 = sys.platform.startswith('win')
+UNDERSCORE_ON_WIN32 = '_' if _WIN32 else ''
+
+os_read = rffi.llexternal(UNDERSCORE_ON_WIN32 + 'read',
+ [rffi.INT, rffi.CCHARP, rffi.SIZE_T],
+ rffi.SSIZE_T, save_err=rffi.RFFI_SAVE_ERRNO)
+
+
class W_FileIO(W_RawIOBase):
def __init__(self, space):
W_RawIOBase.__init__(self, space)
@@ -368,15 +379,38 @@
self._check_readable(space)
rwbuffer = space.getarg_w('w*', w_buffer)
length = rwbuffer.getlength()
- try:
- buf = os.read(self.fd, length)
- except OSError as e:
- if e.errno == errno.EAGAIN:
- return space.w_None
- raise wrap_oserror(space, e,
- exception_name='w_IOError')
- rwbuffer.setslice(0, buf)
- return space.newint(len(buf))
+
+ target_address = lltype.nullptr(rffi.CCHARP.TO)
+ if length > 64:
+ try:
+ target_address = rwbuffer.get_raw_address()
+ except ValueError:
+ pass
+
+ if not target_address:
+ # unoptimized case
+ try:
+ buf = os.read(self.fd, length)
+ except OSError as e:
+ if e.errno == errno.EAGAIN:
+ return space.w_None
+ raise wrap_oserror(space, e,
+ exception_name='w_IOError')
+ rwbuffer.setslice(0, buf)
+ return space.newint(len(buf))
+ else:
+ # optimized case: reading more than 64 bytes into a rwbuffer
+ # with a valid raw address
+ got = os_read(self.fd, target_address, length)
+ got = rffi.cast(lltype.Signed, got)
+ if got >= 0:
+ return space.newint(got)
+ else:
+ err = get_saved_errno()
+ if err == errno.EAGAIN:
+ return space.w_None
+ raise exception_from_errno(space, space.w_IOError, err)
+ keepalive_until_here(rwbuffer)
def readall_w(self, space):
self._check_closed(space)
diff --git a/pypy/module/_io/test/test_fileio.py
b/pypy/module/_io/test/test_fileio.py
--- a/pypy/module/_io/test/test_fileio.py
+++ b/pypy/module/_io/test/test_fileio.py
@@ -1,3 +1,4 @@
+from pypy.interpreter.gateway import interp2app
from rpython.tool.udir import udir
import os
@@ -13,6 +14,11 @@
self.w_posix = self.space.appexec([], """():
import %s as m;
return m""" % os.name)
+ def create_bigfile_w():
+ bigfile = udir.join('bigfile')
+ bigfile.write('a' * 1000, mode='wb')
+ return self.space.wrap(str(bigfile))
+ self.w_create_bigfile = self.space.wrap(interp2app(create_bigfile_w))
def test_constructor(self):
import _io
@@ -154,6 +160,13 @@
f.close()
assert a == 'a\nbxxxxxxx'
+ def test_readinto_optimized(self):
+ import _io
+ a = bytearray('x' * 1024)
+ f = _io.FileIO(self.create_bigfile(), 'r+')
+ assert f.readinto(a) == 1000
+ assert a == 'a' * 1000 + 'x' * 24
+
def test_nonblocking_read(self):
try:
import os, fcntl
@@ -169,6 +182,8 @@
assert f.read(10) is None
a = bytearray('x' * 10)
assert f.readinto(a) is None
+ a2 = bytearray('x' * 1024)
+ assert f.readinto(a2) is None
def test_repr(self):
import _io
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit