[pypy-dev] your thoughts on low level optimizations

Armin Rigo arigo at tunes.org
Thu Sep 1 19:03:39 CEST 2011


Hi Gertjan,

On Thu, Sep 1, 2011 at 1:59 PM, Gertjan van Zwieten
<gertjanvanzwieten at gmail.com> wrote:
> Thanks for the quick reply, this is very helpful information and in some
> ways surprising. Let me just try to confirm that I got all this correctly so
> that I am sure to draw the right conclusions.

The meta-answer first: the problem is that it's still not completely
clear to us which approach is the best.  They all have benefits and
drawbacks so far...

> I was not actually aware that ctypes is considered that efficient. Does this
> apply to CPython as well?

No, that's the first messy part: ctypes code is very efficient on top
of PyPy, at least after the JIT has kicked in.  It is not fast on top
of CPython.

> I always assumed that going by the Python API
> would be the most direct, least overhead interface possible.

By this you probably mean the "CPython API"...  The difference is
important.  The C-level API that you're talking about is really
CPython's.  PyPy can emulate it with the cpyext module, but this
emulation is slow.  Moreover, if you want to compare it with ctypes,
the PyPy JIT gets ctypes *faster* than the CPython C API can ever be
on top of CPython, because the latter needs to explicitly wrap and
unwrap the Python objects.

> Perhaps too generic, but just to fire away all my questions for anyone to
> comment on: what would be the recommended way to raise exceptions going
> through ctypes; special return values, or is there maybe a function call
> that can be intercepted?

The ctypes way to do things is to design the C library with a "normal"
C API, usable from other C programs.  From that point of view the
correct thing is to return error codes, and to check them in pure
Python, after the call to the ctypes function.

> Back on topic, it surprised me, too, that RPython components are not
> modular. Do I understand correctly that this means that, after making
> modifications to the component, the entire PyPy interpreter needs to be
> rebuilt?

Yes.  You should only build RPython modules if you have a specific
reason to.  One example is the numpy module: we want to build it in a
special way so that the JIT can look inside and perform delayed
computations "in bulk".

> Considering the time involved that sounds like a big drawback

This is certainly a drawback, but it's not that big as it first seem.
The RPython module must simply be well-tested as normal Python code
first.  Once it is complete and tested, then we translate it.  It
usually takes a few attempts to fix the typing issues, but once it's
done, it usually works as expected (provided the tests were good in
the first place).

> Are there plans to allow for independently translated modules?
> Or is this somehow fundamentally impossible.

This is a question that comes back regularly.  We don't have any plan,
but there have been attempts.  They have been mostly unsuccessful,
however.  From our point of view we can survive with the drawback, as
it is actually not that big, and as we don't generally recommend to
write RPython modules for everything.

> I must also admit that it is still not entirely clear to me what the precise
> differences are between translated and non-translated code, as in both
> situations the JIT compiler appears to be active. (Right? After all RPython
> is still dynamically typed).

No, precisely, RPython is not dynamically typed.  It is also valid
Python, and as such, it can be run non-translated; but at the same
time, if it's valid RPython, then it can be translated together with
the rest of the interpreter, and we get a statically-typed version of
this RPython code turned into C code.  This translation process works
by assuming (and to a large extent, checking) that the RPython code is
statically typed, or at least "statically typeable"...

> Is there a good text that explains these PyPy fundamentals a little bit more
> entry-level than the RPython Toolchain [1] reference?

The architecture overview is oldish but still up-to-date:
http://doc.pypy.org/en/latest/architecture.html

> Lastly, you mention SWIG of equivalent (Boost?) as alternative options.

These are not really supported so far.  It may be that some SWIG
modules turn into C code that can be loaded by cpyext, but that
doesn't work for Cython, for example.  The case of Cython is
instructive: Romain Guillebert is working right now on a way to take a
Cython module and emit, not C code for the CPython API, but Python
code using ctypes.  This would give a way to "compile" any Cython
module to plain Python that works both of PyPy and CPython (but which
is only fast on PyPy).  We haven't thought so far very deeply about
SWIG.

Reflex is another solution that is likely to work very nicely if you
can rewrite your C module as a C++ module and use the Reflex-provided
Python API extracted from the C++ module.  Again, it's unclear if it's
the "best" path, but it's definitely one path.

> My feelings are that that approach is the most future-proof, which is my
> primary concern before efficiency.

I would say that in this case, keeping your module in C with a
C-friendly API is the most future-proof solution I can think of.  That
means so far --- with today's tools --- that you need to wrap it
twice, as a CPython C extension module and as a pure Python ctypes, in
order to get good performance on both CPython and PyPy.  We hope to be
able to provide better answers in the future, like "wrap it with
Cython and generate the two interfaces for CPython and PyPy
automatically".


A bientôt,

Armin.


More information about the pypy-dev mailing list