It's all tied together because there is an shared value/calling model that permeates both runtime and interpreter and imo with CPython much of it becomes academic because unless people are willing to break source compatibility with native extensions(Esp with Python2 vs Python3 still lingering even if most have gone to P3) it's too much of a historic mess to clean up (Although it's a good sign that the official Python docs points to CFFI and ctypes and discourages new development with the C api).
After doing my thesis work(JS-subset -> C compiler) i was a bit disappointed in the results on weaker processors and it led me to experiment with a series of toy compilers. Those experiments really showed how instrinsic a good value model is to determining your performance _ceiling_.
From my memory you had a factor 3-5 of improving or eliminating each of these:
- bytecode dispatch (vs native code)
- naive dynamic type tests(vs optimized paths)
- obj/refcount management (vs just using values directly with a GC backed model with cheap stack-root-registrations)
- naive property lookups(vs optimized variants).
If you started with a naive dynamic js/python-like language ref counted impl you were within a magnitude of the CPython level of perf, as you check off these checkboxes you get closed to V8 (I used C compilers as backends for code generation so i had a slight edge on the lowlevel code generation part).
The CPython model is great for integrating with hand written C/C++ code but the price you pay is a low ceiling since it requires you to manage ref counts (at the very least at function call boundaries even if you optimize them with deferrals) and as you mention in the article pass values via objects.