Why, oh, why does CPython bother keeping refcounts for small integers? Sure, it lets you make pretty graphs with [sys.getrefcount(i) for i in range(1000)]... but that's an extra memory read and write on every instruction that uses an integer. I can only imagine that not only are these extra instructions, but they're extra instructions that kill pipelining, if the interpreter needed to do "a = 1; b = 1; c = 1" for ins…
There is no such thing as "small integers" in CPython, only PyObjects, of which PyInt_Type and PyLong_Type are implementations. You can't special case integers without changing every C interface that accepts an object, and special casing every piece of code too (i.e. several hundred million lines of 3rd party extension modules, NumPy, lxml, ...). The trade-off is 1 extra machine word per integer (adding to the 2 + he…
Why Python Is Slow: Looking Under the Hood
11–20 of 156 posts
Re: Why Python Is Slow: Looking Under the Hood
#12Why, oh, why does CPython bother keeping refcounts for small integers? Sure, it lets you make pretty graphs with [sys.getrefcount(i) for i in range(1000)]... but that's an extra memory read and write on every instruction that uses an integer. I can only imagine that not only are these extra instructions, but they're extra instructions that kill pipelining, if the interpreter needed to do "a = 1; b = 1; c = 1" for ins…
What you propose sounds like it would be a pure headache for all code which otherwise expects a uniform memory API. Consider a C extension which takes an object and appends it to a list. If small integers did not have a refcount then that extension would have to have special code, like "if object is not a small integer, then increment the reference count".
"Objects with IDs between 0 and 2055, inclusive, are considered unreleasable. The Python runtime shall initialize the refcount of these objects to 1 when they are first used, and shall make no guarantee that the refcount on such objects accurately reflects the true number of references to that object. Native extensions may increase and decrease the refcount on these objects as if they were normal objects; if those extensions are well-behaved, then the refcount should never decrease below 1. However, native extensions that can statically reason about whether an object is likely to be unreleasable, may benefit in performance by checking for whether objects passed to their functions are indeed unreleasable, and refraining from modifying or checking their refcount in such cases."
Re: Why Python Is Slow: Looking Under the Hood
#13Earlier quoted context omitted.
There is no such thing as "small integers" in CPython, only PyObjects, of which PyInt_Type and PyLong_Type are implementations. You can't special case integers without changing every C interface that accepts an object, and special casing every piece of code too (i.e. several hundred million lines of 3rd party extension modules, NumPy, lxml, ...). The trade-off is 1 extra machine word per integer (adding to the 2 + he…
When the GP talks about "small integers", I'm assuming he's talking about numbers between -5 and 256, which are cached ahead of time. They're never going to be garbage collected, so refcounting is largely unnecessary.
Re: Why Python Is Slow: Looking Under the Hood
#14Why, oh, why does CPython bother keeping refcounts for small integers? Sure, it lets you make pretty graphs with [sys.getrefcount(i) for i in range(1000)]... but that's an extra memory read and write on every instruction that uses an integer. I can only imagine that not only are these extra instructions, but they're extra instructions that kill pipelining, if the interpreter needed to do "a = 1; b = 1; c = 1" for ins…
if (number 256) {
decrement_refcount(number);
}Re: Why Python Is Slow: Looking Under the Hood
#15> 2. Python is interpreted rather than compiled. Can we stop saying things like this? Virtually all Python is compiled, as part of the interpretation process. There is a valid point here, of course. The point is that the top Python compilers do not compile to native code. So say that. Words have meanings. Use them correctly.
Nowadays it's the norm for interpreted languages to be JIT-compiled. They still universally call themselves interpreted languages because the jitter is an implementation detail, and the use of one does nothing to negate the characteristics they're trying to advertise when describing themselves as interpreted languages.
Think of it this way: Can I take a text file containing source code in the language and execute it anywhere that has a special program for executing files written in that language (commonly known as an interpreter)? Or do I have to manually perform an intermediate step of converting the program to some binary representation using a special program for doing that (commonly known as a compiler), and then execute the output that program produces?
Re: Why Python Is Slow: Looking Under the Hood
#16Re: Why Python Is Slow: Looking Under the Hood
#17> 2. Python is interpreted rather than compiled. Can we stop saying things like this? Virtually all Python is compiled, as part of the interpretation process. There is a valid point here, of course. The point is that the top Python compilers do not compile to native code. So say that. Words have meanings. Use them correctly.
The reason human language is so expressive is because we can leave out a lot of context and formalism that is required in mathematics and programming languages because the listener/reader will be able to infer it. It's pedantic to expect a writer to hedge against every possible interpretation, when their focus should be on communicating clearly in the first place.
Re: Why Python Is Slow: Looking Under the Hood
#18Earlier quoted context omitted.
What you propose sounds like it would be a pure headache for all code which otherwise expects a uniform memory API. Consider a C extension which takes an object and appends it to a list. If small integers did not have a refcount then that extension would have to have special code, like "if object is not a small integer, then increment the reference count".
I hadn't considered that extensions which needed direct memory access would need access to these objects. But it's a solvable problem. The proposal might read as follows: "Objects with IDs between 0 and 2055, inclusive, are considered unreleasable . The Python runtime shall initialize the refcount of these objects to 1 when they are first used, and shall make no guarantee that the refcount on such objects accurately…
Nor is there much advantage. We know that more advanced implementations can look at program flow and determine that, say, certain operations are only integer based, and therefore unboxable. Or tracing systems can assume that certain types are constant over multiple calls to the same code, and write optimized versions for those types.
These gives much better (eg, >5x according PyPy) optimizations over the error prone change that you believe might be faster.
Re: Why Python Is Slow: Looking Under the Hood
#19[(1 to: 100000000) sum] timeToRun 0:00:00:07.335
Version 4 with new VM due in 2015.
Will perform much much better as with the range used, we need to use LargeIntegers as the 32 bit VM must promote to LargeInteger objects. With the 64bit VM, all fits in.
Re: Why Python Is Slow: Looking Under the Hood
#20> 2. Python is interpreted rather than compiled. Can we stop saying things like this? Virtually all Python is compiled, as part of the interpretation process. There is a valid point here, of course. The point is that the top Python compilers do not compile to native code. So say that. Words have meanings. Use them correctly.
Might want to start by asking the core Python community. Check out the first sentence on wiki.python.org. Nowadays it's the norm for interpreted languages to be JIT-compiled. They still universally call themselves interpreted languages because the jitter is an implementation detail, and the use of one does nothing to negate the characteristics they're trying to advertise when describing themselves as interpreted lang…
The incremental nature of a compiler, then does not make it that we would call it an Interpreter. We would call it an incremental compiler. That's a compiler, which can compile all expression, regardless of the size of the expression, not just 'whole programs'.