10 ms·
Is there any reason there couldn't be a python interpreter as awesome as V8 is for JavaScript?
by umvi 7y ago
Is there any reason there couldn't be a python interpreter as awesome as V8 is for JavaScript?
- gsnedders 7y agoThere's a bunch of things: Python allows metaprogramming in a way that JS does, which means you end up needing more guards (or conflating more guards); the Python ecosystem fairly heavily relies on CPython extension modules, and if you wish to remain compatible with them you're constrained in some ways, especially if you care about performance of calling into/from them.
- matthewaveryusa 7y agoAnd of course money, lots of it. The amount of money invested in optimizing v8 is staggering -- Google brought Lars Bak out of retirement[1] to start v8, and that guy is no joke. [1] https://www.ft.com/content/03775904-177c-11de-8c9d-0000779fd2ac https://www.ft.com/content/03775904-177c-11de-8c9d-0000779fd...
- forgot-my-pw 7y agoPaywalled article
- derefr 7y agoWould there be a performance penalty (I’m guessing in cache coherency) in having an interpreter that’s really two interpreters in the same process, where modules that use the “strict subset” of the language (the part that doesn’t require the more advanced object-model, or any FFI preemption safety) run their code through a more minimal interpreter, and then whenever your code jumps into a module that requires those things, the interpreter itself jumps into a more-complete “fallback” interpreter? Sort of doing what profile-guided JIT optimization does, but without the need for JITing (and before JITing would even kick in), just instead using a little bit of static analysis during the interpreter’s source-parsing step. I ask because I know that this is something hardware “interpreters” (CISC CPU microcode decoders) do, by detecting whether the stream of CISC opcodes in the decode pipeline entirely consist of some particular uarch, and then shunting decode to an optimized decode circuit for that uarch that doesn’t need to consider cases the uarch can’t encode. But, of course, unlike hardware, software interpreters have to try to fit in a CPU’s cache lines and stay branch-predicted, so there might not be a similar win. (Tangent: I once considered writing a compiler that takes Ruby code, rewrites the modules using only a “strict subset” of it to another language, and then either has that language’s runtime host a Ruby interpreter for the fallback, or has the Ruby runtime call the optimized modules through its FFI. I never got far enough into this to determine the performance implications; the plan was actually to enable better concurrency by transpiling Rails web-apps into Phoenix ones, switching out the stack entirely at the framework level and keeping only the “app” code, so single-request performance wasn’t actually the top-level goal.)
- jcranmer 7y ago> Python allows metaprogramming in a way that JS does, which means you end up needing more guards (or conflating more guards) JS allows you to dynamically modify some of the scopes that names refer to, as well as changing the actual prototype chain itself. I'm not sure you can do such crazy things with Python classes/metaclasses. Of course, for v8 in particular, doing any of this crazy manipulation tends to set off alarm klaxons that kick your code off every optimization path, but the language still permits it. > the Python ecosystem fairly heavily relies on CPython extension modules, and if you wish to remain compatible with them you're constrained in some ways, especially if you care about performance of calling into/from them And for JS, very low overhead of calling into the DOM APIs (written in C++) is a necessary feature for having competitive performance. Arguably more so than in Python, since the overhead of the FFI trampoline itself here is considered a bottleneck.
- duckerude 7y ago> dynamically modify some of the scopes that names refer to You can do some fairly disgusting things to name resolution in class bodies, but names within functions are resolved statically nowadays. > as well as changing the actual prototype chain itself You can change a class's MRO, if that's the closest analogue. class Foo: x = 'foo' class Bar: x = 'bar' class Baz(Foo): pass print(Baz.x) Baz.__bases__ = (Bar,) print(Baz.x) In Python you can also hook your own entire custom import system into importlib, or just arbitrarily change the meaning of the `import` statement by replacing builtins.__import__: >>> import builtins >>> builtins.__import__ = lambda *a: "Too bad" >>> import foo >>> foo 'Too bad' You can use sys._getframe() to look in the current call stack and poke at variables: import sys def f(): x = 3 return g() def g(): return sys._getframe().f_back.f_locals['x'] print(f()) # 3 You can make your own class that inherits from types.ModuleType and use it to replace an existing module's class and add interesting new behaviors to its object: # foo.py import sys, types class MyMod(types.ModuleType): def __call__(self): return "Hello!" sys.modules['foo'].__class__ = MyMod >>> import foo >>> foo() 'Hello!' You can replace sys.ps1 by an object with a __str__ implementation to make a dynamic prompt in the REPL: import datetime, sys >>> class Prompt: __str__ = lambda self: str(datetime.datetime.now()) + ' >>>' >>> sys.ps1 = Prompt() 2019-09-12 18:33:48.303692 >>> Python has a lot of exposed detail.
- penagwin 7y agoV8 has the full resources of Google, not to mention Microsoft, Node.js, and every community that uses the V8 engine. Basically V8 has some of the best engineers in the world being paid to work full time on it, and have resources from dozens of other high profile companies. Not knocking python in any way - V8 essentially just has more resources available. I highly recommend trying pypy if you're looking for a performance benefit and your code works with it.
- Scarbutt 7y agoWeb browsers don't rely on python to run, and the Web is important, so Javascript engines get prioritized.
- deleted 7y ago[deleted]
- rudi-c 7y agoI did some work on a Python JIT in the path. The two biggest challenges were: - Python is much, much, more dynamic than Javascript. You can override just about anything in Python, including the meaning of accessing a property. You have overloaded operators (with pretty complex resolution rules), metaclasses, and more. And they're all used extensively. There's some Javascript equivalents to those things, but either there are fewer deoptimization cases or are features that aren't commonly used in practice (e.g. Proxy objects). - Python has a ton of important libraries implemented as C extensions. These libraries tend to depend on undefined behavior of the CPython interpreter (e.g. destruction order which is more deterministic with ref counting) or do things that happen to work but are clearly not supposed to be done (e.g. defining a full Python object as a static variable). I guess also economic incentives, there hasn't been an incentive for anybody to staff a 50 person project to build a Python JIT given that it's cheaper to rewrite some or all of the application in C/C++/Rust/Go whereas that's not an option in Javascriptland.
- oweiler 7y ago> I guess also economic incentives, there hasn't been an incentive for anybody to staff a 50 person project to build a Python JIT given that it's cheaper to rewrite some or all of the application in C/C++/Rust/Go whereas that's not an option in Javascriptland. Ask Dropbox https://github.com/dropbox/pyston https://github.com/dropbox/pyston
- jsnell 7y agoRight, let's ask Dropbox: https://blog.pyston.org/2017/01/31/pyston-0-6-1-released-and-future-plans/ https://blog.pyston.org/2017/01/31/pyston-0-6-1-released-and... (And after that blog post, there are no commits in the github repo you linked to).
- aswanson 7y agoYeah, I was excited when they announced this effort. Sad they 86ed it.
- ufo 7y ago
- chubot 7y agoBesides the existence of __getattr__, __add__, etc. that other people mentioned, there's also: - A Python runtime has to support threads + shared memory, while a JS one doesn't. JS programs are single-threaded (w/ workers). So in this sense writing a fast Python interpreter is harder. - The Python/C API heavily constrains what a Python interpreter can do. There several orders of magnitude more programs that use it than v8's C++ API. For example, reference counts are exposed with Py_INCREF/DECREF. That means it's much harder to use a different reclamation scheme like tracing garbage collection. There are thousands of methods in the API that expose all sorts of implementation details about CPython. Of course PyPy doesn't support all of the API, but that's a major reason why it isn't as widely adopted as CPython. - Python has multiple inheritance; JS doesn't - In Python you can inherit from builtin types like list and dict (as of Python 2.2). In JS you can't. - Python's dynamic type system is richer. typeof(x) in JS gives you a string. type(x) in Python gives you a type object which you can do more with. And common programs/frameworks make use of this introspection. - Python has generators, Python 2 coroutines (send, yield from), and Python 3 coroutines (async/await). In summary, it's a significantly bigger language with a bigger API surface area, and that makes it hard to implement and hard to optimize. As I learn more about CPython internals, I realize what an amazing project PyPy is. They are really fighting an uphill battle.
- Technetium_Hat 7y agoyou can definitely inherit from Array and Map in javascript.
- mikepurvis 7y agoAny future "super speed" Python efforts would probably do well to build on the amazing work that PyPy has done in teasing apart an optimization-friendly subset of the language in the form of RPython, and building the rest of it in that language. Like, focus on further optimizing the RPython runtime rather than starting from scratch.
- umvi 7y ago> optimization-friendly subset of the language in the form of RPython, and building the rest of it in that language. I'm assuming 99% of normal python users are not using anything outside of the RPython subset, correct?
- crtlaltdel 7y agohttps://github.com/iodide-project/pyodide https://github.com/iodide-project/pyodide seems like the round-about path.
- bakery2k 7y agoIsn't Pyodide just an interpreter that runs in the browser? So it could never be significantly faster than the regular Python interpreter?
- jhayward 7y agoPyiodide is the CPython interpreter compiled for WASM rather than e.g. X86. So it's not going to be faster than any other CPython build.
- om2 7y agoMany of the techniques used in modern JS VMs like v8 or JavaScriptCore could totally be applied to a Python interpreter, and it wouldn't take 50 people. Someone just needs to invest the effort. The core techniques are a fast-start interpreter, a templatized baseline JIT, polymorphic inline caches, and runtime type information combined with higher JIT tiers that speculatively optimize for observed types, which allows many checks throughout the generated code to be replaced by side exits. (Also a good garbage collector, and escape analysis to avoid allocating temporaries). I believe most of these could be applicable to Python. JavaScript has crazy levels of dynamism too, and the above methods are the short version of how you deal with it. It seems like no one in the Python community has had the knowledge + motivation to try these approaches.
- jashmatthews 7y agoEvan Phoenix (Rubinius) talked about applying the Self techniques of collecting type info using inline caches for CRuby in 2015. I'm using part of this idea in my prototype tracing JIT for CRuby. With a tracing JIT it's much, much easier to implement basic escape analysis because the control flow is linear. One basically gets Partial Escape Analysis (the big deal from Graal) for free. So far it's proving unreasonably effective to re-use the method lookup info from the inline caches and use the same invalidation mechanism. The CRuby 2.6 JIT can't use this approach because the compilation pipeline is too slow to invalidate it with the method cache, but I'm using the CraneLift compiler from Mozilla. Post Haswell, there's not a ton of value to baseline JIT, especially for dynamic languages. Mike Pall talked at length about how the highly optimized bytecode VM of LuaJIT 2.x was sometimes faster than the baseline-ish method JIT from LuaJIT 1.x, and that was before Haswell. I think we'll see all the major JS engines remove baseline JIT over the next few years in favour of even more optimized bytecode interpreters.
- dralley 7y agoCould you expand on exactly what Haswell did to render baseline pointless?
- jashmatthews 7y ago
- fniephaus 7y agoOracle Labs is working on a Python3 implementation for the GraalVM: https://github.com/graalvm/graalpython https://github.com/graalvm/graalpython
- pron 7y agoI believe that's the purpose of this: https://github.com/graalvm/graalpython https://github.com/graalvm/graalpython
- deckar01 7y agoHave you tried pypy? I got about 2x of C performance for a simulated annealing problem I was working on recently. Ultimately what I realized was that the clever python structures that made prototyping fast were inherently slow (dicts with tuple keys, etc). Once I ported it to C, then went back to python and used the same simple data structures, pypy was practically just as fast as C.
- ltratt 7y agoHistory suggests that, for a good approximation of the truth, the resources put into a language implementation (and, in particular, into JIT compiling VMs) strongly correlate with its performance. So V8 and HotSpot, for example, both have great performance -- and both have had large teams working on them for many years. Interestingly, PyPy has pretty decent performance despite having had a much smaller team working on it, mostly part-time. An interesting thought experiment is whether similar resources put into PyPy -- or a PyPy-like system -- would achieve similar results. My best guess is "yes".