• Kirill Smelkov's avatar
    golang.pyx: Switch pychan from `class` to `cdef class` · 1bcb8297
    Kirill Smelkov authored
    We will need to add C-level attributes to pychan and this requires it to
    become cdef class. The class is exported because at least
    _golang_test.pyx will also need to have access to those attributes.
    
    If we just do `class pychan` -> `cdef class pychan` e.g. the following
    starts to break:
    
        1.venv/local/lib/python2.7/site-packages/py/_path/local.py:701: in pyimport
            __import__(modname)
        golang/__init__.py:174: in <module>
            from ._golang import    \
        golang/_golang.pyx:455: in init golang._golang
            _pychan_send  = _pychan_send.__func__
        E   AttributeError: 'method_descriptor' object has no attribute '__func__'
    
    and
    
        golang/_golang.pyx:513: in golang._golang.pyselect
            if im_class(recv) is not pychan:
        _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    
        f = <built-in method recv of golang._golang.pychan object at 0x7f7055e57cc8>
    
            def im_class(f):
        >       return f.im_class
        E       AttributeError: 'builtin_function_or_method' object has no attribute 'im_class'
    
        golang/_pycompat.py:28: AttributeError
    
    This is probably because for `cdef class` methods Cython does not
    emulate full method bindings the same way as Python does.  Anyway we can
    check which method is passed to pyselect by chanop.__name__ or by
    inspecting PyCFunction directly. And not having method binding wrapper
    should only remove a bit of overhead.
    
    So we are ok with reworking send/recv chanop detection, and since
    this way im_class provided by golang._pycompat becomes unused, it is
    also removed.
    
    The timings are probably within noise:
    
     (on i7@2.6GHz)
    
    thread runtime:
    
        name             old time/op  new time/op  delta
        go               21.7µs ± 1%  20.0µs ± 1%  -7.60%  (p=0.000 n=10+10)
        chan             9.91µs ± 4%  9.37µs ± 4%  -5.39%  (p=0.000 n=10+10)
        select           19.2µs ± 4%  20.2µs ± 4%  +5.62%  (p=0.001 n=9+8)
        def              58.0ns ± 0%  58.0ns ± 0%    ~     (all equal)
        func_def         44.4µs ± 0%  43.8µs ± 1%  -1.22%  (p=0.000 n=10+10)
        call             63.0ns ± 0%  62.4ns ± 1%  -0.95%  (p=0.011 n=10+10)
        func_call        1.05µs ± 1%  1.06µs ± 1%    ~     (p=0.059 n=10+10)
        try_finally       135ns ± 0%   136ns ± 0%  +0.74%  (p=0.000 n=10+9)
        defer            2.36µs ± 1%  2.28µs ± 1%  -3.59%  (p=0.000 n=10+10)
        workgroup_empty  49.0µs ± 1%  48.2µs ± 1%  -1.63%  (p=0.000 n=10+9)
        workgroup_raise  62.6µs ± 1%  58.9µs ± 1%  -5.96%  (p=0.000 n=10+10)
    
    gevent runtime:
    
        name             old time/op  new time/op  delta
        go               21.7µs ± 1%  20.5µs ± 1%  -5.33%  (p=0.000 n=10+9)
        chan             9.91µs ± 4%  9.72µs ± 5%    ~     (p=0.190 n=10+10)
        select           19.2µs ± 4%  19.5µs ±14%    ~     (p=0.968 n=9+10)
        def              58.0ns ± 0%  58.0ns ± 0%    ~     (all equal)
        func_def         44.4µs ± 0%  45.4µs ± 1%  +2.23%  (p=0.000 n=10+10)
        call             63.0ns ± 0%  64.0ns ± 0%  +1.59%  (p=0.000 n=10+10)
        func_call        1.05µs ± 1%  1.06µs ± 0%  +0.65%  (p=0.002 n=10+10)
        try_finally       135ns ± 0%   137ns ± 0%  +1.48%  (p=0.000 n=10+10)
        defer            2.36µs ± 1%  2.38µs ± 1%  +0.72%  (p=0.006 n=10+10)
        workgroup_empty  49.0µs ± 1%  48.2µs ± 1%  -1.65%  (p=0.000 n=10+10)
        workgroup_raise  62.6µs ± 1%  60.3µs ± 1%  -3.69%  (p=0.000 n=10+10)
    1bcb8297
_golang.pyx 20.3 KB