upvote
> 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__`:

    >>> class Dog:
    ...     def hi(self):
    ...             print("woof")
    ...
    >>> class Cat:
    ...     def hi(self):
    ...             print("meow")
    ...
    >>> dog = Dog()
    >>> dog.hi()
    woof
    >>> dog.__class__ = Cat
    >>> dog.hi()
    meow
    >>> isinstance(dog, Dog)
    False
    >>> isinstance(dog, Cat)
    True
    >>>
reply
I've been doing things in Python for over 20 years. Only recently I learn that I can do this.

    >>> Dog.__bases__ = (Cat,)
    >>> isinstance(dog, Dog)
    True
    >>> isinstance(dog, Cat)
    True
    >>>
reply
This is wild. Is it part of Python's specification, or an implementation detail of CPython?
reply
As far as I understand, CPython implementation is THE specification. Some billions dollar ERP system firmly rely on this behavior, btw.
reply
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

reply
The thing that actually makes it harder in Python is threading, because it breaks all ability to reason locally about what might happen and optimize. In Python you have to assume that another thread may do these things at any point mid-execution.
reply
I see no issue here.

Modifying a variable that is read by another thread needs special care in all myltithreaded languages I know. If you don't use a synchronization primitive to tell the other thread "hey, this thing here may have changed, read it again from memory"; the other thread is free to use its old value for as long as it whishes.

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.
reply
Smalltalk, Self and Lisp Machines enter the bar, with live image changes.
reply
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.
reply
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.

reply
That works fine as long as nobody actually uses the unhappy path. If major libraries and frameworks do it all the time, you lose. So there is a certain aspect of culture outside what the language itself is capable of doing.
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.
reply
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.
reply
See Self.
reply