Hello, HPy
hpyproject.org
hpyproject.org
Only complaint from a quick scan -- carrying over that horrid PyArg_Parse() API. It's huge, needs varargs, isn't type safe, repeatedly rebuilds strings to look them up in kwargs, and isn't even all that convenient to use. Also, if you haven't dealt with a SEGV due to PyArg_Parse() before, it means you probably have never tried to write an extension
PyPy will become about 1000x more practical if something like this ever makes it into Cython. That would make it easy to bring over huge swathes of existing extensions like lxml.
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
Isn't that (plus, also, cross-interpreter native extensions) what the FFI [0] gem (first implemented for Rubinius, then JRuby, and now pretty much every Ruby interpreter that matters except Opal.)
AFAIK, FFI has been the recommended way of writing C-exts for Ruby for sometime, as FFI exts are both cleaner to write and portable across Ruby implementations.
However, note that the FFI isn't really applicable with one very common use-case of C extensions anyway - increased performance through reaching through abstractions. See fast_blank for example. Can you reimplement that effectively using a cross-platform FFI? I'm not sure you can.
The FFI also adds a bit of runtime overhead for each call, which you may not want.
Or grab the fn that the FFI points to and lightly decompile and inline into the interpreter?
Compile the typed subset of dynamic language of the day into Wasm and inline that into the interpreter?
I am sure something here is the Holy Grail of native-ish extension mechanism.
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.
Lua’s C API is generally considered to be well-designed, and does something similar with `lua_State`. How does the HPy API compare to the Lua API more generally?
CPython (the default interpreter) has had support for multiple interpreters in the same process since 1997, but it has only been exposed to the C API, and not in the language itself. Python 3.10, coming out later this year, will expose multiple interpreters in one process (subinterpreters, see PEP 554 [1]).
I'm excited about what could eventually come out of this. If there is one GIL per interpreter, we could have something like the `multiprocessing` library for parallel execution, but all within one process.
[0] https://www.tcl-lang.org/man/tcl8.6/ThreadCmd/thread.htm
Encapsulated interpreter state via HPyContext would allow replacing the per-process lock with a per-interpreter lock, enabling such parallelism.
Passing messages between Threads can be done asynchronously which does not block either thread.
Example Tcl program with threads
package require Thread
set tid [thread::create]
thread::send -async $tid {
while true { after 200; puts Jazz }
}
while true { after 100; puts Party }
This creates one thread which is in an infinite loop printing "Jazz" to stdout every 200ms, and the "main" thread which is in an infinite loop printing "Party" to stdout every 100ms.In Lua API, all Lua values are bound to a stack, and C functions have to exchange values via that stack - there's no free-standing "Lua value" type in the API. Thus, the runtime is fully aware of all the objects that flow back and forth, even if one native Lua function is calling another native Lua function.
That's not 100% correct. You can have values unbound from the stack, they're just typed, rather than having one "value" type. (lua_Number, lua_CFunction, etc). Though functions bound from do still use the stack for taking/returning values. (However your C functions can directly work on other value types).
"There is no way to convert the pointer back to its original value. Typically this function is used only for debug information."
(In CPython, you can e.g. stash a PyObject* away in C globals.)
Your first stop will be luaL_ref [0].
The registry object really is just an address. It's the equivalent of: lua_State->top+LUA_REGISTRYINDEX[reference] in C.
There's no functional difference to a PyObject pointer.
I get the feeling ppl don't use it mostly because of the "uncool" factor...
Would you use SWIG to extend python or other languages? Why or why not?
1: https://www.geeksforgeeks.org/wrapping-cc-python-using-swig-...
I got burned so bad that I ditched all efforts at automatic binding generations from there on out. Debugging this thing was impossible (to me, a long long time ago) and I ended up writing manual bindings to python & matlab instead. Those two were the important targets anyhow, and it allowed me to really think through how I exposed & handed over the different chunks of memory being allocated in the native code.
- People actually use the "hard to implement" parts of the C API
- Moving things from macros to function calls can often be quite detrimental to performance
- C extensions have been co-optimized with the C API, so changing the C API will make things less optimized
- These "check if runtime debugging is enabled" checks are not free
My guess is that this will end up in a tough middle ground where extension writers will be faced with a tradeoff along the lines of making their extension 10% slower for 99% of their users in order to make things 2x faster for 1% of their users.
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
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.
A C extension is far preferable when you want to code in C, either to write a new data type[1], or write a Python frontend to a C library[2] that is too complex to be well supported by simple FFI.
I think people use Cython more internally when they value the maintainability of "mostly Python" over the fact that it's slower than what native C would get them.
Or any other language that supports C FFI.
You have a quote for that? Cython code, in the context of "C-extended-python" probably never is slower than native C. There has been a lot of work over the years to make sure of that.
Now, if you want to compare it to "a C program/library in the wild", then we are not comparing apples to apples. The whole point of Cython is inserting C code into a Python module, to have (high performance) C code inside a Python environment.
Cython makes you do more work to avoid the interpreter. C makes you do more work to involve the interpreter.
> There has been a lot of work over the years to make sure of that.
Some cython code can compile close to the C code you'd write.
But that's not how it's likely to be used, because the design pushes you in the opposite direction by default, and the value of Cython is that it's far faster than vanilla Python, and the painless interop.
Huh? Cython tries, but it's not exactly optimized hand-coded C. And it works better for some things (e.g. numerical typed code) than others.
It's not a "benefit over" thing, they're tools operating at completely different levels and thus not necessarily exclusive.
cython binds to the C API of CPython, meaning you've got the same problem loading a cython-compiled extension into alternative implementations as you have with other CPython extensions.
If HPy succeeds, cpython can (hopefully) have an HPy backend and generate HPy bindings, somewhat transparently.
https://www.boost.org/doc/libs/1_72_0/libs/python/doc/html/t...
https://www.boost.org/doc/libs/1_72_0/libs/python/doc/html/b...