3 ms·
Javascript has different runtime properties where it counts. You can't subclass numbers or inspect the stack in JS, which you can do in Python. These (bad, IMO)
by noelwelsh 3y ago
Javascript has different runtime properties where it counts. You can't subclass numbers or inspect the stack in JS, which you can do in Python. These (bad, IMO) design decisions make Python harder to optimize than JS.
- tonyg 3y agoThis doesn't follow. Both Self and Smalltalk allow you to do those things. (You still need to specialise code for plain small ints vs oops either way for the subclassing one, and stack inspection can be done on a slow path, see the dynamic deoptimization stuff)
- noelwelsh 3y agoIt's not impossible, just more work. V8 would be something like 100 man years of work, and that's with a language that is somewhat easier to optimize.
- tonyg 3y agoI don't think it's even more work. (V8 has the dynamic deopt stuff already...) But at this point I guess we're dueling reckons. Python is hard to optimize in a semantics-preserving way, of course, because the semantics are so bonkers. I just don't think it's stuff like subclassing int or inspecting the stack that makes it difficult.
- noelwelsh 3y agoI can agree with this take. What about Python do think makes it hard to optimize?
- tonyg 3y agoHmm. Off the top of my head, mind, but: - The bad API to foreign code that all the extensions depend on. - The nuts scoping rules, though I suppose throw enough static analysis at it and you might be OK. - Reference counting, which people depend upon. - locals(), globals(), though you could do some tricks with deoptimization there too I suppose. - Its metaclass system is pretty wacky. In general, just the overall ugliness of the design. It'd be jolly difficult to get beyond a toy operational model of the language, for example. There are so many corner cases to get right. Compare, say, Smalltalk, where there's an actual aesthetic to the metasystem (in all its 80s glory), and a full implementation can be remarkably small and simple; or anything else where you could plausibly capture an operational semantics without too much fuss.
- tonyg 3y agoOh! Neat: Shriram's group wrote a 2013 paper on semantics for Python. It's roughly what I would have expected it to be. It doesn't look like it addresses mutation of module bindings, for example, but perhaps the paper just doesn't show enough of the full model they built, or I'm misunderstanding the desugaring they are using. https://cs.brown.edu/~sk/Publications/Papers/Published/pmmwplck-python-full-monty/paper.pdf https://cs.brown.edu/~sk/Publications/Papers/Published/pmmwp...
- rollcat 3y agoWhile I can't argue about Python's design decisions impacting the ability to optimise it (not my area of expertise), I'd like to defend these decisions. Subclassing numbers (or other primitive types) is one way to achieve the goal of having type-safe enumerations. In Python, I can do: class Weekday(int): pass Monday = Weekday(1) ... The value x=1 will not pass the test of isinstance(x, Weekday); mypy won't let you accidentally pass April as a Weekday; etc. There definitely are many ways to make this prettier, or easier for an optimising compiler, but I would like to echo DJB's talk referenced by the original article: > Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. Likewise, the ability to inspect the stack enables creation of useful tools, that would be difficult or impossible otherwise. When working with pytest, I can write "assert my_array == [1, 2, 3]"; when my_array contains an extra element at the end, my test fails with a pretty-printed diff of the two sides. You don't need to perform aggressive whole-program optimisation passes when running a test suite, and you shouldn't need to inspect the stack at runtime while executing performance-critical sections in production. I save time and energy by not having to add "print(my_array)", re-running the tests, and copy-pasting the pretty-printed values for diffing. I save time and energy when my program has less bugs, because I had the tools to make it more correct.
- noelwelsh 3y agoI agree those are useful tools, but I think the same goals can be achieved with different tools that don't make optimization hard. For example, type safe enums are one use of so-called algebraic data types found in Rust / Scala / Swift etc. (The language feature is called an enum in these languages, but it can represent a lot more than what people usually think of as an enumeration.)
- rollcat 3y ago100% agree on everything you said. However it's easy to judge Python's mistakes through the lens of where we are right now - nobody even considered tracing JITs for dynamic languages before V8 showed up, and without this perspective, subclassing int could look like the more ergonomic choice. Python could have had ADTs as a core language feature 20+ years ago (rather than sloppily grafting them on in a recent release), just like it could have had unicode strings as the default text type since before 2.x (1.6 was 2000; unicode was early 1990s). But it also had a much smaller community back then, and who knows how making these choices back then would've impacted its development? Maybe it'd end up being as relatively obscure as say Tcl. Also I think you could still achieve reasonable optimisations even if you do go that route; Go has type aliases (including on primitive types) that you can define custom methods on: type Weekday int const ( Monday = Weekday(1) ... ) func (w Weekday) Name() string { switch w { case Weekday(1): return "Monday" ... default: panic("?") } } If your compiler could prove nothing crazier than that is happening with your int subclass, I don't see why similarly written Python code should have a lower performance ceiling; but yes, that's more work for the compiler.