Live data from Hacker News

How many lines of C it takes to execute a + b in Python

codeconfessions.substack.com

171–180 of 224 posts

Re: How many lines of C it takes to execute a + b in Python

#171

Earlier quoted context omitted.

I am about to turn 38 and I got it, so it's at least slightly lower than 40.

Hey Steve, loved your work on Rust and your goodbye letter. Nice to see you in my comment chain

Thanks!

Re: How many lines of C it takes to execute a + b in Python

#172

Earlier quoted context omitted.

Yes, it's a very tootsie-roll-center kind of answer, but it's clearly more than a few. To answer your question, 15kLoC is more than enough to implement dynamic dispatch and the PyObject base struct, along with the special method logic for __add__ on any python object type.. but still a lot less than what's needed for all the special method types and a lot of boilerplate for the C-compatible interface around those met…

I wonder what percent of this audience got the tootsie-roll reference. Does anyone under 40 know it?

I'm in Gen Z and I think I got it. The Tootsie pop commercial with the owl used to play all the time on Canadian TV.

Re: How many lines of C it takes to execute a + b in Python

#173
post #122

Earlier quoted context omitted.

Unfortunately Python's innard is much more complicated than most expectations. You have named JS and Lua, but those languages never have "magic" methods---JS instead has prototypes and more recently proxies, while Lua has metatables. Ordinary objects aren't magic in this sense, and conversely magical objects are generally deliberate choices in those languages. But Python's magic `__dunder__` methods are everywhere in…

In the late 1990s and early 200xs, there were a lot of claims that there was no such thing as a "slow language", that all languages can be run as quickly as C if you just built a sufficiently smart compiler and/or runtime. I haven't heard anyone make this claim in a while. The inability to speed up Python beyond a certain point despite a lot of clever approaches taken was probably a good chunk of the reason, the rema…

> In the late 1990s and early 200xs, there were a lot of claims that there was no such thing as a "slow language", that all languages can be run as quickly as C if you just built a sufficiently smart compiler and/or runtime.

I think "slow language" never meant that way. It was more like a counterpoint to the claim that there are inherent classes of languages in terms of performance, so that some language is (say) 100x or 1000x slower than others in any circumstances. This is not true, even for Python. Most languages with enough optimization works can be made performant enough that it's no slower than 10x C. But once you've got to that point, it can take disproportionally more works to optimize further depending on specific designs.

> If I were designing a language to be slow, but not like stupidly slow just to qualify as an esolang, but where the slowness still contributed to things I could call "features" with a straight face, it would be hard to beat Python.

Ironically, Python's relative slowness came from its uniform design which is generally a good thing. This is distinct from a TCL-style "everything is a string" you've said, because the uniform design had a good intent by its own.

If you have used Python long enough you may know that Python originally had two types of classes---there was a transition period where you had to write `class Foo(object):` to get the newer version. Python wanted to remove a blur between builtin objects and user objects and eventually did so. But no one at that time knew that the blur is actually a good thing for optimization. Python tried to be a good language and is suffering today as a result.

Re: How many lines of C it takes to execute a + b in Python

#174
post #4

A while back someone posted their patch to cpython where they replaced the hash function with a fast one and claimed this dramatically sped up the whole Python runtime. They claimed that the hash function was used constantly —e.g. 11 times in print("hello world")—because it's used to look up object properties. Apparently the default implementation is not optimized for performance but for security, just in case the so…

What's more, you can get a significant speedup from your Python scripts by replacing the inbuilt cpython malloc calls with a static "allocate big chunk of stack at the beginning and I'll manage it myself" implementation, falling back to malloc as needed if it grows beyond that. A college class in perf engineering I TA'd did this, the results even a beginner could achieve were compelling, the top of the class produced…

I'd love to see an example of this if you happen to have one available. I hit a startup-time issue in a previous life, and I wish I had spent the time looking at it back then

Re: How many lines of C it takes to execute a + b in Python

#175

Earlier quoted context omitted.

Unfortunately Python's innard is much more complicated than most expectations. You have named JS and Lua, but those languages never have "magic" methods---JS instead has prototypes and more recently proxies, while Lua has metatables. Ordinary objects aren't magic in this sense, and conversely magical objects are generally deliberate choices in those languages. But Python's magic `__dunder__` methods are everywhere in…

Lua has metamethods: https://www.lua.org/manual/5.3/manual.html#2.4

They reside in metatables, which are only optionally linked to ordinary objects. Python magic methods are a part of the core object protocol.

Re: How many lines of C it takes to execute a + b in Python

#176

Earlier quoted context omitted.

Unfortunately Python's innard is much more complicated than most expectations. You have named JS and Lua, but those languages never have "magic" methods---JS instead has prototypes and more recently proxies, while Lua has metatables. Ordinary objects aren't magic in this sense, and conversely magical objects are generally deliberate choices in those languages. But Python's magic `__dunder__` methods are everywhere in…

Also, you have craziness like quite regular iteration being implemented using exceptions, which are not exactly trivial to optimize.

If you want to be return absolutely any value from an iterator, an exception is indeed a reasonable choice though. Python generators came in much later, unlike JS for example.

Re: How many lines of C it takes to execute a + b in Python

#177
post #4

A while back someone posted their patch to cpython where they replaced the hash function with a fast one and claimed this dramatically sped up the whole Python runtime. They claimed that the hash function was used constantly —e.g. 11 times in print("hello world")—because it's used to look up object properties. Apparently the default implementation is not optimized for performance but for security, just in case the so…

> I'd much prefer to have a "I'm offline, please run twice as fast!" If I know anything about programmers, it's that everyone would just use the "go faster" flag by default.

Yet ~ nobody uses PyPy by default. Speed is clearly not the top consideration for most Python programmers.

Re: How many lines of C it takes to execute a + b in Python

#178

Earlier quoted context omitted.

The semantic issues of making a performant Python language implementation are more or less exactly the same as for JS and Lua, optimizing Ruby seems to possibly have even more "magic" that needs patching but we've seen the Shopify team get cracking on that (it includes MaximeCB that did HiggsJS). PyPy is in many aspects to be rated as a research project that tried a novel approach to reduce the workload compared to t…

Unfortunately Python's innard is much more complicated than most expectations. You have named JS and Lua, but those languages never have "magic" methods---JS instead has prototypes and more recently proxies, while Lua has metatables. Ordinary objects aren't magic in this sense, and conversely magical objects are generally deliberate choices in those languages. But Python's magic `__dunder__` methods are everywhere in…

Smalltalk, SELF and Common Lisp are full of dynamic magic, you can in a single function call change the representation of all instances of a given object during program execution, at any given time break into the debugger and change whatever you feel like and resume execution, dynamically load code from the network with side effects on the running program, and many other crazy things.

Yet, not only are they in the genesis of JIT compiler research, their JITs are quite good, and their results went directly into JavaScript JITs research.

Re: How many lines of C it takes to execute a + b in Python

#179
post #174

Earlier quoted context omitted.

What's more, you can get a significant speedup from your Python scripts by replacing the inbuilt cpython malloc calls with a static "allocate big chunk of stack at the beginning and I'll manage it myself" implementation, falling back to malloc as needed if it grows beyond that. A college class in perf engineering I TA'd did this, the results even a beginner could achieve were compelling, the top of the class produced…

I'd love to see an example of this if you happen to have one available. I hit a startup-time issue in a previous life, and I wish I had spent the time looking at it back then

A template repo can be found here https://github.com/JacksonKearl/cpython, but it does not implement an ideal malloc, just a baseline one - I am not sure if it is still being used as an assignment.

The repo states that even this dummy implementation:

> has a 60% faster startup as compared to base CPython, and in some test cases has marginally better runtime performance as well.

Re: How many lines of C it takes to execute a + b in Python

#180
post #136

Earlier quoted context omitted.

Can someone explain what exactly it is about Python's design that makes it slow? What changes would have to be made to speed it up? Obviously changing its core design now would break things, but my question is, can we can imagine an alternate universe Python that's as close as possible to our Python, except really fast? What would be different?

It's the whole design. Every object creates allocation/gc overhead, bytecode dispatch is a major bottleneck, attribute lookup is expensive, objects are expensive, namespaces are expensive, etc. You can change things internally (e.g. optimizing opcode parsing), fixed object layouts, restricting mutability, converting everything to predictable array accesses, but you'll likely just end up with something like Lua or Wre…

Smalltalk, Common Lisp and SELF are just as dynamic if not more, with a JIT.
Post reply on HN