antocuni
> Why do blue functions disappear when redshifting? If you red shift blue then it goes down in frequency so you might get green or red. Perhaps my physics brain is just over thinking it!
yes I think you are overthinking :). It's not meant to be accurate physics of course.
The usage of colors to distinguish comptime vs runtim code comes from PyPy: in that context, we used "green" and "red", and initial versions of SPy used the same convention.
Then someone pointed out that green/red is not colorblind friendly and so I changed it to blue.
Having actual colors for the two phases is VERY useful for visualization: e.g. we already have a "spy --colorize" command which shows you which parts are blue and which are red.
As for "redshifting": the AST "before" has a mixture of blue and red colors, while the AST "after" has only red nodes, thus the final AST is "more red" than the first one, that's why I chose that name.
> how can you reliably import libraries?
the blog post specifies it but probably not in great level of detail. Calling python libs from spy will go through libpython.so (so essentially we will embed CPython). So CPython will import the library, and there will be a SPy<=>CPython interop layer to convert/proxy objects on the two worlds.
The problem of cpyext is that it's super slow, for good reasons: https://pypy.org/posts/2018/09/inside-cpyext-why-emulating-c...
There are efforts to create a new C API which is more friendly to alternative implementations (including CPython itself, when they want to change how they do things internally): https://hpyproject.org/ https://github.com/py-ni
The biggest differences between the two JITs are: 1. PyPy is meta tracing, CPython is tracing 2. PyPy has "standard" code generation backends, CPython has copy&patch. 3. CPython so far uses "trace projection" while PyPy uses "trace recording".
(1+2) make CPython JIT much faster to compile and warmup than PyPy, although I suspect that most of the gain is because of (1). However, this comes at the expense of generality, because in PyPy you can automatically trace across all builtins, whereas in CPython you are limited to the bytecode.
Trace projection looked very interesting to me because it automatically solve a problem which I found everywhere in real world code: if you do trace recording, you don't know whether you will be actually able to close the loop, and so you must decide to give up after a certain threshold ("trace too long"). The problem is that there doesn't seem to be threshold which is generally good, so you always end up tracing too much (big warmup costs + literally you are doing unnecessary work) or not enough (the generated code is less optimal, sometimes up to 5-10x).
With trace projection you decide which loop to optimize "in retrospect" so you don't have that specific problem. However, you have OTHER problems (in particular that you don't know the actual values used in the trace) which makes it harder to optimize, so CPython JIT plans to switch to trace recording.
One big advantage of tracing JITs is that they are generally easier to write an to maintain. For the specific case of PyPy, it's actually a "meta tracing JIT": you trace the interpreter, not the underlying program, which TL;DR means that you can write the interpreter (which is "easy") and you get a JIT compiler for free. The basic assumption of a tracing JIT is that you have one or more "hot loops" in which you have one (or few) fast paths which are taken most of the time.
If the assumption holds, tracing has big advantages because you eliminate most of dynamism and you automatically inline across multiple layer of function calls, which in turns make it possible to eliminate allocation of most temporary objects. The problem is that the assumption not always holds, and that's where you start to get problems.
But methods JITs are not THE solution either. Meta has a whole team developing Cinder, which is a method JIT for Python, but they had to introduce what they call "static python", which is an opt-in sets of constraints to remove some Python dynamism to make the JIT job easier.
Finally, as soon as you call any C extension, any JIT is out of luck and must deoptimize to present a "state of the world" which is compatible with that the C extension finds.
Claude code modified my .bashrc without asking
You can check by yourself seeing of the spy compiler translates the code to C in the (super-experimental) spy playground: https://antocuni.pyscriptapps.com/spy-playground/latest/
HPy proves that it is indeed possible to have high-performance C extensions with PyPy: https://pypy.org/posts/2019/12/hpy-kick-off-sprint-report-18...
The PyPy Blog Turns 15 years
I agree with your concerns, HPy tries to address them since the beginning. Basically, there are two distinct compilation modes: - CPython ABI: in this mode, things like HPy_Dup and HPy_Close are translated directly into Py_INCREF and Py_DECREF. The overhead is 0 both in theory and in practice, since all the benchmark that we ran so far confirmed this.
- HPy Universal ABI: in this mode, you introduce the indirections which makes it possible e.g. the debug mode. Our benchmarks indicate a 5-10% overhead, which is in line with what you (and we :)) expected.
So, if you are an extension writer, you will be able to distribute both CPython-opimized and universal binaries
One nice effect of such an API is that under certain conditions, implementations will be allowed to bypass argument parsing entirely: e.g., if I have a function which takes two C longs, the PyPy JIT will be able to emit a call directly to it, without having to box/unbox the arguments just to do the call as it happens right now.
However, on the other hand we will need to provide an API similar to the existing PyArg_ParseTuple, because we want to make it easy to migrate existing extensions
In theory, most of the porting could be automated. The only thing which cannot is turning Py_INCREF/DECREF into HPy_Dup/HPy_Close: they are closely related, but HPy requires that you close each handle independently, contrarily to Python/C where you can DECREF the same PyObject* multiple times, as long as the final refcount is correct.
For that, the debug mode will be very useful because it precisely tells you which handles you didn't close and which ones you closed multiple times.
The main Cython developer (Stefan Behnel) was present during the very first few meetings where we started to talk about HPy and gave positive feedback on that.