List indexing is not that fast on Python 2 too:
(py2) python -m perf timeit -s 's=[0]' 's[0]'
Mean +- std dev: 31.7 ns +- 0.9 ns
(py2) python -m perf timeit -s 's={0:0}' 's[0]'
Mean +- std dev: 44.5 ns +- 1.1 ns
vs. Python 3: (py3.6) python -m perf timeit -s 's=[0]' 's[0]'
Mean +- std dev: 36.7 ns +- 0.8 ns
(py3.6) python -m perf timeit -s 's={0:0}' 's[0]'
Mean +- std dev: 41.1 ns +- 1.0 nsPython's int/long unification was done in Python 2.2 (PEP 237). Which by the way, I think it's a great feature, even if it makes it slower. That is what Python is about after all: simplicity and ease of use over performance. If you really need the max performance possible, don't use Python.
As far as I knew, the only thing that changed in Python 3 was that they renamed the long type to int, and they also removed L suffix. But from your code seems that other things have changed as well and now two structures are used for short integers. Do you know when this happened exactly and why?
> the only thing that changed in Python 3 was that they renamed the long type to int
Python 2.x had two types: int and long. Transitions from int to long were pretty seamless and the two types were somewhat compatible. This is not unification: there were still two separate types.
Python 3 has one type called int that functions mostly like long. You can sort of say (I guess) that "long was renamed to int", but only if you say "and int was removed": really, the types were unified.
In Python/ceval.c, BINARY_SUBSCR can no longer easily have a fast path for tuple and list indexing with integers. This makes somelist[0] about as slow as somedict[0]:
Likewise, the builtin sum() function lost speed on its fast path for summing integers. That path is still there but it now uses the slow PyLong_AsLongAndOverflow() function instead of the PyInt_AS_LONG() fast macro. IMO, Python 3 int/long unification should have just dropped the trailing "L" from the display of long integers and called it done.From the user's point of view, that would have unified the two types enough to mostly not care. You would still have two different types for int and long, but otherwise the switch back and forth was already somewhat seamless in Python 2.7.
The range of single cell ints was enormous (up to 9,223,372,036,854,775,807 or 2^63-1). It fulfilled typical use cases without spilling into slower and more complex multi-cell calculations and without impeding useful downstream optimizations for common cases.
Once Python 3 was released and the API guaranteed that all integers were the same type regardless of size, that design choice was set in stone. So, now we will have to live with it forever :-(