Author: Carl Friedrich Bolz-Tereick <cfb...@gmx.de> Branch: Changeset: r93739:6a370242b86c Date: 2018-02-02 22:24 +0100 http://bitbucket.org/pypy/pypy/changeset/6a370242b86c/
Log: merge 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. 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,11 @@ 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. 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/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') _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit