6 ms·
Very interesting and quite ... instructive. The lack of optimizations is interesting. One would naively imagine that treating, e.g., ints, strings, dicts, and l
by choeger 6y ago
Very interesting and quite ... instructive. The lack of optimizations is interesting. One would naively imagine that treating, e.g., ints, strings, dicts, and lists special would gain some performance.
- kevin_thibedeau 6y agoSmall integers do get special treatment as the same values share the same object.
- Pxtl 6y agoOkay, that's dodging the object instantiation/destruction thing but it's still doing all the reference counting on them isn't it?
- saagarjha 6y agoWhy would reference counting be necessary an immutable, immortal global?
- Pxtl 6y agoIt isn't, but IIRC refcounting is cooked into the python interpreter in so many places that everything that can be referenced must also be refcounted. If you get a reference to something, you increment its refcount. If you lose the reference, you decrement it. No ifs, no branches.
- masklinn 6y agoAFAIK the implementation of cpython is purposefully kept relatively simple such that it remains approachable.
- ketralnis 6y agoThere are fast-paths for these cases, like this one: https://github.com/python/cpython/blob/4b8cdfcb22fbeaab9d954cb693a7fb3362a382b6/Python/ceval.c#L1831-L1833 https://github.com/python/cpython/blob/4b8cdfcb22fbeaab9d954... (and note the comment above it about integer addition!) But the cpython folk make understandability an explicit goal of the codebase which tends to preclude things like fancy optimisations or JITs
- Pxtl 6y agoYeah. The Python C implementation's simplicity is a double-edge sword. It's delightfully easy to extend despite being straight C, but it also has made some frustrating performance trade-offs. Last time I checked, something as simple as myObj.MyMethod() would be implemented as (this is from memory and I'm rusty): myObj is fetched from the local scope array by array-index. Dictionary lookup on myObj failing over to dictionary lookup on myObj.__class__ to find the method MyMethod(). Or was it one merged dict? Whichever. All __slots__ does is mean that myObj doesn't need its own dictionary, it still has to go to __class__ for a dictionary hit, which can even be overridden if they've changed __getattr__ or __getattribute__. Originally cpython even used deliberately-high-collision hashtables for lookups for reasons I no longer remember (faster sort?). MyMethod() instantiates a new Method object that stores the underlying class-function and the "self" parameter, but the object is pooled, so it's quick. Still, it's creating a reference that will have to be collected in a moment. Then we actually invoke the damned function. Then we decrement the refcount on the Method object, which drops it to zero and it is destroyed (returning it to object-pool). Obviously, this is from my memory and it's been over a decade, so I might be getting details wrong, but it was surprised that there were so many unoptimized layers involved in resolving a method. That even with __slots__ dictionary hits were unavoidable, and that method invocation involved instantiating an object (from a pool, but still).
- markdown 6y agoYou comment just reminds me of how lucky some people are to be born with ...IMO... the super-power of good memory. > Obviously, this is from my memory and it's been over a decade I would not remember those details if I read them last week let alone a decade ago.
- dj_mc_merlin 6y agoNot if you read them.
- Pxtl 6y agoWell, it's because I was trying to implement something that leveraged them heavily, so I got pretty intimately involved with it. Obviously if I'd just been reading up I wouldn't remember any of this.