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…
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".
> 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".
Easily implemented in one place in the "increment_refcount(obj)" inline function:
// roughly speaking
if (is_heap_pointer(obj))
obj->refcount++;
where "is_heap_pointer(obj)" is an inlined bitmask check like (((unsigned int) obj) & TAG_MASK) == TAG_PTR)
If TAG_PTR is zero bits, then a value which satisifes is_heap_pointer can be dereferenced straight.In a garbage collection implementation, you don't have refcounts, but the garbage collector's "mark object" function does the same check:
if (!is_heap_pointer(obj))
return; // don't try to mark non-heap things
switch (type(obj)) {
case TYPE_CONS_CELL
mark_obj(obj->cons.car);
mark_obj(obj->cons.cdr);
break;
// ...
}
Another thing is that you provide an API to the extension writers, which abstracts the use of objects. For instance, you can give them a function that can be called like this: value n = number(42);
value z = number(INT_MAX);
The first case might construct an unboxed value because the integer is small enough. But perhaps the second returns a bignum because INT_MAX requires 32 bits, wheres unboxed integers only go up to 30 bits.So in the first case you get an object with no refcount, whereas in the second you get an object with a refcount of 1. The extension code is written such that it doesn't care.