That's about right. That's about where Unladen Swallow maxed out. To get beyond that point, you have to do more global analysis and JIT compilation and recompilation, as PyPy does.
Every Python symbol reference requires a dictionary lookup. Every function call and operator requires that the type of the left hand side be examined for dispatching purposes. More than 99% of the time, the symbol and type will be the same as last time, and it's a huge win to assume that it will be and compile code for that case. You still have to be prepared for the times when it isn't, and have a backup system. That's basically what PyPy does.
More stuff is mutable in Python than really needs to be mutable.