4 ms·
It's due to the runtime. JavaScript has a runtime that updates as it executes. It notices a loop is being run frequently and then compiles a new version of th
by brrrrrm 4y ago
It's due to the runtime.
JavaScript has a runtime that updates as it executes. It notices a loop is being run frequently and then compiles a new version of the loop that runs faster.
Python doesn't have this functionality.
WebAssembly is for running things in the browser and would slow everything down (Python, JS, C++). The tests in the post are run natively (on my command line).
- deleted 4y ago[deleted]
- cogman10 4y agoPython does have this functionality, but it's hamstrung by the implementation. Javascript is, for the most part, completely sandboxed and separate. Which means the JIT for Javascript is totally free to do neat things like move an object from one region of memory to another. CPython, on the other hand, has to care about things like "was this object created in C? Is it pinned to that memory location? Does this method end up delegating to C?" And you see that in the complexity it takes to introduce native methods in both. Moving and using data from JS to Webassembly is a PITA. Accessing data in a Python object from c ( and vice versa) is trivial. That triviality is what gets in the way of JIT optimizations.
- brrrrrm 4y agoI don't buy this argument. WebAssembly is a vastly different beast than trivial C extensions (since WASM is a browser thing). JavaScript has both and is still much faster. Bun (the runtime I used for the post), has a native C interface that's far simpler to use than Python (even with pybind/nanobind): https://github.com/oven-sh/bun#bunffi-foreign-functions-interface https://github.com/oven-sh/bun#bunffi-foreign-functions-inte... Deno (a v8-based runtime) has the same thing: https://deno.land/manual@v1.25.4/runtime/ffi_api https://deno.land/manual@v1.25.4/runtime/ffi_api These use the standard C-ABI directly without requiring headers (unlike Python). Node.js standardized a more Python-like C API called Node-API: https://nodejs.org/api/n-api.html https://nodejs.org/api/n-api.html I believe Python can continue to add performance and is not hamstrung by the implementation.
- cogman10 4y agoThe difference between all of these and how the python c api works is that 1 layer of abstraction and translation. Take the deno example, in order to invoke a C function you have to tell deno "load this lib, this dynamic function is here, and the method signature looks like this". Look at what types are permitted to be passed between the two as well. deno isn't exposing deno objects to C, it's exposing only simple primitive types. The cpython FFI, on the other hand grants C full access to python objects. That one layer of abstraction and distance makes all the difference and is hard to backport in. There's a reason alternative pythons (pypy, graalpy) don't really support it and struggle to support libs that rely heavily on it (like tensorflow).
- brrrrrm 4y agoYou can expose objects. Here's how it is done in Bun: https://github.com/facebookresearch/shumai/blob/main/shumai/tensor/tensor.ts#L277 https://github.com/facebookresearch/shumai/blob/main/shumai/... We've been using this feature heavily in Shumai. I think you are vastly overestimating the complexity associated with this (user exposed ref-counting/garbage collection) and may not be totally up to date on what's implemented.
- cogman10 4y ago> I think you are vastly overestimating the complexity associated with this No, I think you are misunderstanding the problem. I'm not saying that, with a good 3rd party lib, generating FFI APIs can't be easy and slick. I'm saying that non-python languages have more complicated FFI APIs that practically necessitate these sorts of generation libraries (like bun). When you grab a bit of memory out of an object in C with python, you are reaching directly into python's internal representation of the object and tickling the bits there. When you do that with deno/javascript/others, there's a layer of abstraction introduced to keep our native method from directly tickling the bits that the VM is aware of. That's the problem. From C, you can create a new python object, store it off in a global variable, send it back to the python vm, and later in a thread go tickle some of the bits and see that tickling in the Python VM. Because that object is the same one used by Python and C. The complication that arises with CPython is many very popular librarys (numpy, tensorflow, pandas) rely HEAVILY on the fact that the objects they are working with in C are the same ones python uses. That's why they've been so slow to port to pypy if at all. And that ability for C libraries to very deeply interact with the VM is exactly the problem that makes it hard to improve CPython's JIT. That's the reason other python JITs, like pypy, either don't or have very limited support for CPython's FFI capabilities. The reason FFI works so well with other languages is they drew very tight and clear boundaries around how interactions work and who owns what when. So please, stop spamming "bun". It's a non sequitur.
- amelius 4y ago> Accessing data in a Python object from c ( and vice versa) is trivial. That triviality is what gets in the way of JIT optimizations. Can't it be automated? Accessing data is often done in patterns that are very similar. Perhaps we should access Python from Rust, would that help?
- cogman10 4y agoAbsolutely, and it usually is. The FFI isn't generally hard to use because of the automation.
- squeaky-clean 4y agoJust some extra info to add to this for OP/others. The keyword to Google to learn more about this would be a "JIT compiler". If you were to run each of those benchmarks to loop only a few times (or steps for the n-body example), say n=10, they would appear much slower because the JIT compiler needs lines of code to be run multiple times before it can begin making optimizations. "pypy" is an alternative runtime for Python which can do JIT optimizations like Javascript interpreters do. Another commenter in a different subthread already posted a quick speed comparison involving pypy, it's much faster than the standard python runtime (called CPython). But pypy isn't necessarily a synonym for Python (which almost always means CPython). It doesn't have 100% compatibility with 3rd party libraries (it's probably 99.9% but just 1 unsupported dependency is enough to prevent your whole project from going pypy). And also with most Python projects, the python code isn't the slow part of the stack. If you're making a web app, your cache and database is probably where 99% of your request time is spent. For scientific computing, data analysis, or ai training, you're usually using 3rd party libraries written in a "fast" language like C/Fortran/Rust with python hooks, and your python just acts as glue code to pass data between these library calls.