[pypy-commit] pypy default: merge heads
cfbolz
pypy.commits at gmail.com
Sun Feb 4 07:53:16 EST 2018
Author: Carl Friedrich Bolz-Tereick <cfbolz at gmx.de>
Branch:
Changeset: r93753:03e7d032c07f
Date: 2018-02-04 13:52 +0100
http://bitbucket.org/pypy/pypy/changeset/03e7d032c07f/
Log: merge heads
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -23,3 +23,16 @@
added, then the performance using mapdict is linear in the number of
attributes. This is now fixed (by switching to a regular dict after 80
attributes).
+
+
+.. branch: cpyext-faster-arg-passing
+
+When using cpyext, improve the speed of passing certain objects from PyPy to C
+code, most notably None, True, False, types, all instances of C-defined types.
+Before, a dict lookup was needed every time such an object crossed over, now it
+is just a field read.
+
+
+.. branch: 2634_datetime_timedelta_performance
+
+Improve datetime + timedelta performance.
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -208,6 +208,21 @@
def _set_mapdict_storage_and_map(self, storage, map):
raise NotImplementedError
+
+ # -------------------------------------------------------------------
+ # cpyext support
+ # these functions will only be seen by the annotator if we translate
+ # with the cpyext module
+
+ def _cpyext_as_pyobj(self, space):
+ from pypy.module.cpyext.pyobject import w_root_as_pyobj
+ return w_root_as_pyobj(self, space)
+
+ def _cpyext_attach_pyobj(self, space, py_obj):
+ from pypy.module.cpyext.pyobject import w_root_attach_pyobj
+ return w_root_attach_pyobj(self, space, py_obj)
+
+
# -------------------------------------------------------------------
def is_w(self, space, w_other):
diff --git a/pypy/module/_io/test/test_interp_textio.py b/pypy/module/_io/test/test_interp_textio.py
--- a/pypy/module/_io/test/test_interp_textio.py
+++ b/pypy/module/_io/test/test_interp_textio.py
@@ -1,6 +1,6 @@
import pytest
try:
- from hypothesis import given, strategies as st
+ from hypothesis import given, strategies as st, settings
except ImportError:
pytest.skip("hypothesis required")
import os
@@ -29,6 +29,7 @@
@given(data=st_readline(),
mode=st.sampled_from(['\r', '\n', '\r\n', '']))
+ at settings(deadline=None)
def test_readline(space, data, mode):
txt, limits = data
w_stream = W_BytesIO(space)
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -10,6 +10,8 @@
PyVarObject, Py_ssize_t, init_function, cts)
from pypy.module.cpyext.state import State
from pypy.objspace.std.typeobject import W_TypeObject
+from pypy.objspace.std.noneobject import W_NoneObject
+from pypy.objspace.std.boolobject import W_BoolObject
from pypy.objspace.std.objectobject import W_ObjectObject
from rpython.rlib.objectmodel import specialize, we_are_translated
from rpython.rlib.objectmodel import keepalive_until_here
@@ -21,6 +23,52 @@
#________________________________________________________
# type description
+class W_BaseCPyObject(W_ObjectObject):
+ """ A subclass of W_ObjectObject that has one field for directly storing
+ the link from the w_obj to the cpy ref. This is only used for C-defined
+ types. """
+
+
+def check_true(s_arg, bookeeper):
+ assert s_arg.const is True
+
+def w_root_as_pyobj(w_obj, space):
+ from rpython.rlib.debug import check_annotation
+ # make sure that translation crashes if we see this while not translating
+ # with cpyext
+ check_annotation(space.config.objspace.usemodules.cpyext, check_true)
+ # default implementation of _cpyext_as_pyobj
+ return rawrefcount.from_obj(PyObject, w_obj)
+
+def w_root_attach_pyobj(w_obj, space, py_obj):
+ from rpython.rlib.debug import check_annotation
+ check_annotation(space.config.objspace.usemodules.cpyext, check_true)
+ assert space.config.objspace.usemodules.cpyext
+ # default implementation of _cpyext_attach_pyobj
+ rawrefcount.create_link_pypy(w_obj, py_obj)
+
+
+def add_direct_pyobj_storage(cls):
+ """ Add the necessary methods to a class to store a reference to the py_obj
+ on its instances directly. """
+
+ cls._cpy_ref = lltype.nullptr(PyObject.TO)
+
+ def _cpyext_as_pyobj(self, space):
+ return self._cpy_ref
+ cls._cpyext_as_pyobj = _cpyext_as_pyobj
+
+ def _cpyext_attach_pyobj(self, space, py_obj):
+ self._cpy_ref = py_obj
+ rawrefcount.create_link_pyobj(self, py_obj)
+ cls._cpyext_attach_pyobj = _cpyext_attach_pyobj
+
+add_direct_pyobj_storage(W_BaseCPyObject)
+add_direct_pyobj_storage(W_TypeObject)
+add_direct_pyobj_storage(W_NoneObject)
+add_direct_pyobj_storage(W_BoolObject)
+
+
class BaseCpyTypedescr(object):
basestruct = PyObject.TO
W_BaseObject = W_ObjectObject
@@ -66,8 +114,12 @@
def realize(self, space, obj):
w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
+ assert isinstance(w_type, W_TypeObject)
try:
- w_obj = space.allocate_instance(self.W_BaseObject, w_type)
+ if w_type.flag_cpytype:
+ w_obj = space.allocate_instance(W_BaseCPyObject, w_type)
+ else:
+ w_obj = space.allocate_instance(self.W_BaseObject, w_type)
except OperationError as e:
if e.match(space, space.w_TypeError):
raise oefmt(space.w_SystemError,
@@ -76,6 +128,9 @@
w_type)
raise
track_reference(space, obj, w_obj)
+ if w_type.flag_cpytype:
+ assert isinstance(w_obj, W_BaseCPyObject)
+ w_obj._cpy_ref = obj
return w_obj
typedescr_cache = {}
@@ -186,12 +241,12 @@
Ties together a PyObject and an interpreter object.
The PyObject's refcnt is increased by REFCNT_FROM_PYPY.
The reference in 'py_obj' is not stolen! Remember to decref()
- it is you need to.
+ it if you need to.
"""
# XXX looks like a PyObject_GC_TRACK
assert py_obj.c_ob_refcnt < rawrefcount.REFCNT_FROM_PYPY
py_obj.c_ob_refcnt += rawrefcount.REFCNT_FROM_PYPY
- rawrefcount.create_link_pypy(w_obj, py_obj)
+ w_obj._cpyext_attach_pyobj(space, py_obj)
w_marker_deallocating = W_Root()
@@ -237,7 +292,7 @@
@jit.dont_look_inside
def as_pyobj(space, w_obj, w_userdata=None, immortal=False):
"""
- Returns a 'PyObject *' representing the given intepreter object.
+ Returns a 'PyObject *' representing the given interpreter object.
This doesn't give a new reference, but the returned 'PyObject *'
is valid at least as long as 'w_obj' is. **To be safe, you should
use keepalive_until_here(w_obj) some time later.** In case of
@@ -245,7 +300,7 @@
"""
assert not is_pyobj(w_obj)
if w_obj is not None:
- py_obj = rawrefcount.from_obj(PyObject, w_obj)
+ py_obj = w_obj._cpyext_as_pyobj(space)
if not py_obj:
py_obj = create_ref(space, w_obj, w_userdata, immortal=immortal)
#
diff --git a/pypy/module/cpyext/test/test_typeobject.py b/pypy/module/cpyext/test/test_typeobject.py
--- a/pypy/module/cpyext/test/test_typeobject.py
+++ b/pypy/module/cpyext/test/test_typeobject.py
@@ -3,10 +3,20 @@
from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
from pypy.module.cpyext.test.test_api import BaseApiTest
from pypy.module.cpyext.api import generic_cpy_call
-from pypy.module.cpyext.pyobject import make_ref, from_ref, decref
+from pypy.module.cpyext.pyobject import make_ref, from_ref, decref, as_pyobj
from pypy.module.cpyext.typeobject import PyTypeObjectPtr
class AppTestTypeObject(AppTestCpythonExtensionBase):
+
+ def setup_class(cls):
+ AppTestCpythonExtensionBase.setup_class.im_func(cls)
+ def _check_uses_shortcut(w_inst):
+ res = hasattr(w_inst, "_cpy_ref") and w_inst._cpy_ref
+ res = res and as_pyobj(cls.space, w_inst) == w_inst._cpy_ref
+ return cls.space.newbool(res)
+ cls.w__check_uses_shortcut = cls.space.wrap(
+ gateway.interp2app(_check_uses_shortcut))
+
def test_typeobject(self):
import sys
module = self.import_module(name='foo')
@@ -157,6 +167,25 @@
assert fuu2(u"abc").baz().escape()
raises(TypeError, module.fooType.object_member.__get__, 1)
+ def test_shortcut(self):
+ # test that instances of classes that are defined in C become an
+ # instance of W_BaseCPyObject and thus can be converted faster back to
+ # their pyobj, because they store a pointer to it directly.
+ if self.runappdirect:
+ skip("can't run with -A")
+ module = self.import_module(name='foo')
+ obj = module.fooType()
+ assert self._check_uses_shortcut(obj)
+ # W_TypeObjects use shortcut
+ assert self._check_uses_shortcut(object)
+ assert self._check_uses_shortcut(type)
+ # None, True, False use shortcut
+ assert self._check_uses_shortcut(None)
+ assert self._check_uses_shortcut(True)
+ assert self._check_uses_shortcut(False)
+ assert not self._check_uses_shortcut(1)
+ assert not self._check_uses_shortcut(object())
+
def test_multiple_inheritance1(self):
module = self.import_module(name='foo')
obj = module.UnicodeSubtype(u'xyz')
More information about the pypy-commit
mailing list