Comment by PathOfEclipse
18 hours ago
How many Python JIT experiments have failed? I find it interesting because Ruby is probably harder to optimize than Python but Ruby has had a JIT for a long time. Maybe base python is more optimized than base Ruby so the JIT is incrementally not changing much? Also, I'd expect javascript to be harder to optimize but it has JITs. That could just be a funding issue, though. There's probably much more money and incentive to optimize Javascript.
The type of dynamic language gymnastics done with Python is huge: any sufficiently well known library basically employs some sort of meta-programming of the language:
JavaScript never had that level of flexibility (for better and for worse), and by extension, it's easier to build an alternative implementation and keep it compatible with the rest of the world that claims to be JavaScript.
> The type of dynamic language gymnastics done with Python is huge
Have you seen Ruby?
Ruby is essentially Smalltalk (see also Objective-C).
I don't know Python's innards well enough to understand why it's so especially difficult in this regard, but I gather around the edges that its object model may not be as well founded.
1 reply →
The common excuse that ignores Smalltalk, Self, Lisp Machines like Interlisp-D, and more recently Ruby.
All just as dynamic as Python, and with quite usable JIT compilers.
Or for that matter, PyPy and GraalPy do exist.
Obviously, I am not saying making a JIT for Python is impossible: both PyPy and GraalPy prove otherwise. We are talking about full ecosystem support, not just the base language and stdlib.
Can you just replace CPython with PyPy or GraalPy in every project? And why not? :)
2 replies →
Operator overloading is a non-issue, it's just another form of method calls.
JavaScript also has monkey-patching, and it's also very common. E.g. SolidJS replaces objects with dependency-tracking proxies.
There are really no fundamental language reasons why Python should be more complex. From what I heard from a JIT developer, the main issue is not the code itself but the module system and code loading.
JavaScript was designed from the start to operate inside a sealed environment with only few external handles, with the code loaded upfront or added on-demand via "eval". Not so for Python, where native code modules can (and will) result in code loaded dynamically and it's hard to track what depends on what.
See Self and Smalltalk for such kind of highly dynamic environment with code being loaded, and completely changing the running image on the fly.
Yet both languages are on the genesis of modern JIT compilers, going beyond what was already being done for Lisps, Prolog, BASIC.
> JavaScript also has monkey-patching, and it's also very common. E.g. SolidJS replaces objects with dependency-tracking proxies.
And the proxies make it slow as Python. Plain objects cannot suddenly become proxies, so the runtime can treat them as a special case.
> Many libraries depend on monkey-patching live library calls, including ones to external dependencies
someone please think of the monkey patchers!
Python is harder to JIT because of the way __dict__ works.
If all python had was __slots__ then we would not be struggling to JIT python.
Tight numerical loops is where the JIT adds performance.
Most performance sensitive features are already available in python between numpy, numba and Cython without really leaving the language core.
You'll mostly be speeding up the slow parts of python, not supercharging the fast parts.
> Python is harder to JIT because of the way __dict__ works.
Heh, how about how `__class__` works? Not that I recommend actually doing this, but you can literally change the type of an already instantiated object by reassigning `__class__`:
I've been doing things in Python for over 20 years. Only recently I learn that I can do this.
2 replies →
That's also possible in JS with __proto__. The prototypes of the classes in the chain can be mutated at runtime too. How to handle these cases is well understood for JITs though and python could do the same.
See also the warning here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
And this related article:
https://mathiasbynens.be/notes/prototypes
1 reply →
Lua has the same property with metatables. LuaJIT, which is very fast, has a solution: if you change a metatable after assigning it, or reassign a metatable, Mike Pall turns his back on you. Your code dies slowly, in the outer darkness of the interpreter loop, never to be JIT’ted upon again.
Smalltalk, Self and Lisp Machines enter the bar, with live image changes.
I've never bought this argument. Underneath all of the magic inheritance in Python is just the MRO (method resolution order). That's not characteristically any different from JavaScript's prototype chain. There's no reason a Python JIT couldn't optimize everything JavaScript does, and if you do specifically access `self.__dict__`, that can be a cold, unoptimizable path.
One of the tricks here is to treat the happy path as a quasi-invariant and leave a guard that branches to slow code if the invariant didn’t hold.
I saw this presented as new tech at either OOPSLA or OSCon almost exactly 20 years ago.
CPU branch prediction is still pretty good even with the loss of function in recent years.
1 reply →
I'd say that's about right. The way people usually code in Python, mro is quite stable and most lookups would be cache hits. You can still get more weird invalidation in Python cuz they let you change stuff like `__bases__`, or you get some weird `__getattribute__` change in some place that would force you to invalidate more than you'd initially think should be necessary.
Same as for the common lisp or raku mop. You check for the default, and use the fast path if so. And if you can guarantee that nobody changed those keys or values, lift that check to the front.
See Self.
> How many Python JIT experiments have failed?
Wait, how many? I'd say just this one. The other that I know of, pypy, was massively successful for a single maintainer project. And definitely showed the possibility/potential of JIT python.
I wouldn't call it a failure for not over taking mainline CPython.
> Maybe base python is more optimized than base Ruby
Not since before Ruby 1.9, no.