Author: Antonio Cuni <anto.c...@gmail.com>
Branch: 
Changeset: r92538:9f9989a1ffe6
Date: 2017-10-01 22:25 +0200
http://bitbucket.org/pypy/pypy/changeset/9f9989a1ffe6/

Log:    merge the cpyext-jit; this makes cpyext calls up to 7x faster in
        very simple cases, see whatsnew for details

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
@@ -4,3 +4,12 @@
 
 .. this is a revision shortly after release-pypy2.7-v5.9.0
 .. startrev:899e5245de1e
+
+.. branch: cpyext-jit
+
+Differentiate the code to call METH_NOARGS, METH_O and METH_VARARGS in cpyext:
+this allows to write specialized code which is much faster than previous
+completely generic version. Moreover, let the JIT to look inside the cpyext
+module: the net result is that cpyext calls are up to 7x faster. However, this
+is true only for very simple situations: in all real life code, we are still
+much slower than CPython (more optimizations to come)
diff --git a/pypy/module/_cffi_backend/cffi1_module.py 
b/pypy/module/_cffi_backend/cffi1_module.py
--- a/pypy/module/_cffi_backend/cffi1_module.py
+++ b/pypy/module/_cffi_backend/cffi1_module.py
@@ -1,4 +1,5 @@
 from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rlib import jit
 
 from pypy.interpreter.error import oefmt
 from pypy.interpreter.module import Module
@@ -15,7 +16,7 @@
 
 INITFUNCPTR = lltype.Ptr(lltype.FuncType([rffi.VOIDPP], lltype.Void))
 
-
+@jit.dont_look_inside
 def load_cffi1_module(space, name, path, initptr):
     # This is called from pypy.module.cpyext.api.load_extension_module()
     from pypy.module._cffi_backend.call_python import get_ll_cffi_call_python
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -449,6 +449,11 @@
         if func.__name__ in FUNCTIONS_BY_HEADER[header]:
             raise ValueError("%s already registered" % func.__name__)
         func._always_inline_ = 'try'
+        #
+        # XXX: should we @jit.dont_look_inside all the @cpython_api functions,
+        # or we should only disable some of them?
+        func._jit_look_inside_ = False
+        #
         api_function = ApiFunction(
             argtypes, restype, func,
             error=_compute_error(error, restype), gil=gil,
diff --git a/pypy/module/cpyext/methodobject.py 
b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -1,4 +1,5 @@
 from rpython.rtyper.lltypesystem import lltype, rffi
+from rpython.rlib import jit
 
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.error import OperationError, oefmt
@@ -42,8 +43,8 @@
     from pypy.module.cpyext.object import _dealloc
     _dealloc(space, py_obj)
 
-
 class W_PyCFunctionObject(W_Root):
+    # TODO create a slightly different class depending on the c_ml_flags
     def __init__(self, space, ml, w_self, w_module=None):
         self.ml = ml
         self.name = rffi.charp2str(rffi.cast(rffi.CCHARP,self.ml.c_ml_name))
@@ -56,7 +57,7 @@
             w_self = self.w_self
         flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags)
         flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST)
-        if space.is_true(w_kw) and not flags & METH_KEYWORDS:
+        if not flags & METH_KEYWORDS and space.is_true(w_kw):
             raise oefmt(space.w_TypeError,
                         "%s() takes no keyword arguments", self.name)
 
@@ -96,6 +97,20 @@
         else:
             return space.w_None
 
+class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject):
+    def call(self, space, w_self, w_args, w_kw):
+        # Call the C function
+        if w_self is None:
+            w_self = self.w_self
+        func = self.ml.c_ml_meth
+        return generic_cpy_call(space, func, w_self, None)
+
+class W_PyCFunctionObjectSingleObject(W_PyCFunctionObject):
+    def call(self, space, w_self, w_o, w_kw):
+        if w_self is None:
+            w_self = self.w_self
+        func = self.ml.c_ml_meth
+        return generic_cpy_call(space, func, w_self, w_o)
 
 class W_PyCMethodObject(W_PyCFunctionObject):
     w_self = None
@@ -215,6 +230,7 @@
                                   (self.method_name,
                                    self.w_objclass.name))
 
+@jit.dont_look_inside
 def cwrapper_descr_call(space, w_self, __args__):
     self = space.interp_w(W_PyCWrapperObject, w_self)
     args_w, kw_w = __args__.unpack()
@@ -225,10 +241,22 @@
         space.setitem(w_kw, space.newtext(key), w_obj)
     return self.call(space, w_self, w_args, w_kw)
 
+def cfunction_descr_call_noargs(space, w_self):
+    # special case for calling with flags METH_NOARGS
+    self = space.interp_w(W_PyCFunctionObjectNoArgs, w_self)
+    return self.call(space, None, None, None)
 
+def cfunction_descr_call_single_object(space, w_self, w_o):
+    # special case for calling with flags METH_O
+    self = space.interp_w(W_PyCFunctionObjectSingleObject, w_self)
+    return self.call(space, None, w_o, None)
+
+@jit.dont_look_inside
 def cfunction_descr_call(space, w_self, __args__):
+    # specialize depending on the W_PyCFunctionObject
     self = space.interp_w(W_PyCFunctionObject, w_self)
     args_w, kw_w = __args__.unpack()
+    # XXX __args__.unpack is slow
     w_args = space.newtuple(args_w)
     w_kw = space.newdict()
     for key, w_obj in kw_w.items():
@@ -236,6 +264,7 @@
     ret = self.call(space, None, w_args, w_kw)
     return ret
 
+@jit.dont_look_inside
 def cclassmethod_descr_call(space, w_self, __args__):
     self = space.interp_w(W_PyCFunctionObject, w_self)
     args_w, kw_w = __args__.unpack()
@@ -276,6 +305,26 @@
     )
 W_PyCFunctionObject.typedef.acceptable_as_base_class = False
 
+W_PyCFunctionObjectNoArgs.typedef = TypeDef(
+    'builtin_function_or_method', W_PyCFunctionObject.typedef,
+    __call__ = interp2app(cfunction_descr_call_noargs),
+    __doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc),
+    __module__ = interp_attrproperty_w('w_module', 
cls=W_PyCFunctionObjectNoArgs),
+    __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs,
+        wrapfn="newtext_or_none"),
+    )
+W_PyCFunctionObjectNoArgs.typedef.acceptable_as_base_class = False
+
+W_PyCFunctionObjectSingleObject.typedef = TypeDef(
+    'builtin_function_or_method', W_PyCFunctionObject.typedef,
+    __call__ = interp2app(cfunction_descr_call_single_object),
+    __doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc),
+    __module__ = interp_attrproperty_w('w_module', 
cls=W_PyCFunctionObjectSingleObject),
+    __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject,
+        wrapfn="newtext_or_none"),
+    )
+W_PyCFunctionObjectSingleObject.typedef.acceptable_as_base_class = False
+
 W_PyCMethodObject.typedef = TypeDef(
     'method_descriptor',
     __get__ = interp2app(cmethod_descr_get),
diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py
--- a/pypy/module/cpyext/modsupport.py
+++ b/pypy/module/cpyext/modsupport.py
@@ -1,11 +1,13 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import cpython_api, cpython_struct, \
-        METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING
+        METH_STATIC, METH_CLASS, METH_COEXIST, CANNOT_FAIL, CONST_STRING, \
+        METH_NOARGS, METH_O
 from pypy.module.cpyext.pyobject import PyObject, as_pyobj
 from pypy.interpreter.module import Module
 from pypy.module.cpyext.methodobject import (
     W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod,
-    PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New)
+    PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New,
+    W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
 from pypy.module.cpyext.state import State
 from pypy.interpreter.error import oefmt
@@ -79,6 +81,13 @@
                       space.newtext(rffi.charp2str(doc)))
     return w_mod   # borrowed result kept alive in PyImport_AddModule()
 
+def _create_pyc_function_object(space, method, w_self, w_name, flags):
+    flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST)
+    if flags == METH_NOARGS:
+        return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name)
+    if flags == METH_O:
+        return W_PyCFunctionObjectSingleObject(space, method, w_self, w_name)
+    return W_PyCFunctionObject(space, method, w_self, w_name)
 
 def convert_method_defs(space, dict_w, methods, w_type, w_self=None, 
name=None):
     w_name = space.newtext_or_none(name)
@@ -98,7 +107,8 @@
                     raise oefmt(space.w_ValueError,
                             "module functions cannot set METH_CLASS or "
                             "METH_STATIC")
-                w_obj = W_PyCFunctionObject(space, method, w_self, w_name)
+                w_obj = _create_pyc_function_object(space, method, w_self,
+                                                    w_name, flags)
             else:
                 if methodname in dict_w and not (flags & METH_COEXIST):
                     continue
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
@@ -14,7 +14,7 @@
 from rpython.rlib.objectmodel import specialize
 from rpython.rlib.objectmodel import keepalive_until_here
 from rpython.rtyper.annlowlevel import llhelper
-from rpython.rlib import rawrefcount
+from rpython.rlib import rawrefcount, jit
 from rpython.rlib.debug import fatalerror
 
 
@@ -151,6 +151,7 @@
 class InvalidPointerException(Exception):
     pass
 
+@jit.dont_look_inside
 def create_ref(space, w_obj, w_userdata=None, immortal=False):
     """
     Allocates a PyObject, and fills its fields with info from the given
@@ -190,6 +191,7 @@
 
 w_marker_deallocating = W_Root()
 
+@jit.dont_look_inside
 def from_ref(space, ref):
     """
     Finds the interpreter object corresponding to the given reference.  If the
@@ -227,6 +229,7 @@
     assert isinstance(w_type, W_TypeObject)
     return get_typedescr(w_type.layout.typedef).realize(space, ref)
 
+@jit.dont_look_inside
 def as_pyobj(space, w_obj, w_userdata=None, immortal=False):
     """
     Returns a 'PyObject *' representing the given intepreter object.
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -410,6 +410,7 @@
     ptr = get_new_method_def(space)
     ptr.c_ml_meth = rffi.cast(PyCFunction, llslot(space, tp_new_wrapper))
 
+@jit.dont_look_inside
 def is_tp_new_wrapper(space, ml):
     return ml.c_ml_meth == rffi.cast(PyCFunction, llslot(space, 
tp_new_wrapper))
 
diff --git a/pypy/module/pypyjit/policy.py b/pypy/module/pypyjit/policy.py
--- a/pypy/module/pypyjit/policy.py
+++ b/pypy/module/pypyjit/policy.py
@@ -14,7 +14,7 @@
             return True
         if '.' in modname:
             modname, rest = modname.split('.', 1)
-            if modname in ['unicodedata', 'gc', '_minimal_curses', 'cpyext']:
+            if modname in ['unicodedata', 'gc', '_minimal_curses']:
                 return False
         else:
             rest = ''
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to