Earlier quoted context omitted.
It's great software for the use case that Python is intended for . Python is supposed to be glue code. You embed a scripting runtime in your application, do all the heavy lifting in C, but configure your building blocks in Python so that you can easily reconfigure them as needs change. NumPy, SciPy, TensorFlow, PyTorch, JAX, Pandas, Pillow, lxml, cjson, PyCapnP, Tornado, fast-avro, etc. all get it right. They are wra…
Arguably no one’s goal is to glue pieces of C together. That’s too abstract. Their goal is to do something like write a performant web server, maybe easily and quickly. Python’s approach is one way to do that, but there are other ways that might be better. Having to write code in two languages to solve a problem, one of which that is difficult to write and has lots of footguns, has some clear downsides.
How many lines of C it takes to execute a + b in Python
141–150 of 224 posts
Re: How many lines of C it takes to execute a + b in Python
#142Earlier quoted context omitted.
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…
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?
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 Wren rather than Python, and people like Python specifically because of the ecosystem that's built up around that dynamicism over the years.
Re: How many lines of C it takes to execute a + b in Python
#143Earlier quoted context omitted.
Python rather happened to have a language design and C API design that doesn't allow a simple performant implementation. Even PyPy was not that fast compared to other JIT implementations, while its C API support was always subpar. It is easy to say that Python should trade them off for performance, but they are one of the key reasons for Python's success after all.
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…
It's pretty well established that "Mike Pall" is the pen name for an AI sent from the future for unknown reasons. It disappeared from our light cone due to a rift in causality, presumably because it succeeded in whatever changes it wanted to make in the future.
Re: How many lines of C it takes to execute a + b in Python
#144Earlier 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…
That is why the "as fast as C with the sufficiently smart compiler" never truly came to pass in a general sense, even if many languages that were slow to start have gotten way faster with better implementations.
Re: How many lines of C it takes to execute a + b in Python
#145Earlier quoted context omitted.
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…
"Tech debt doesn't matter!" "We're going to redesign it the right way after we get this version out the door!"
Re: How many lines of C it takes to execute a + b in Python
#146Earlier quoted context omitted.
It's great software for the use case that Python is intended for . Python is supposed to be glue code. You embed a scripting runtime in your application, do all the heavy lifting in C, but configure your building blocks in Python so that you can easily reconfigure them as needs change. NumPy, SciPy, TensorFlow, PyTorch, JAX, Pandas, Pillow, lxml, cjson, PyCapnP, Tornado, fast-avro, etc. all get it right. They are wra…
Arguably no one’s goal is to glue pieces of C together. That’s too abstract. Their goal is to do something like write a performant web server, maybe easily and quickly. Python’s approach is one way to do that, but there are other ways that might be better. Having to write code in two languages to solve a problem, one of which that is difficult to write and has lots of footguns, has some clear downsides.
Re: How many lines of C it takes to execute a + b in Python
#147Earlier quoted context omitted.
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…
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?
If I write a function like this in Rust:
pub fn add(x: i32, y: i32) -> i32 {
x + y
}
this will compile to this assembly (on x86_64): add:
leal (%rdi,%rsi), %eax
retq
two instructions. This is because in Rust, free functions exist, have a name, and they are called by name. There's an additional twist here though too, let's check it out in debug mode, with optimizations off: add:
subq $24, %rsp
movl %edi, 16(%rsp)
movl %esi, 20(%rsp)
addl %esi, %edi
movl %edi, 12(%rsp)
seto %al
testb $1, %al
jne .LBB0_2
movl 12(%rsp), %eax
addq $24, %rsp
retq
.LBB0_2:
leaq str.0(%rip), %rdi
leaq .L__unnamed_1(%rip), %rdx
movq core::panicking::panic@GOTPCREL(%rip), %rax
movl $28, %esi
callq *%rax
ud2
There's a few things going on here, but the core of it is that in Rust, in debug mode, overflow of addition is checked, but in release mode, wrapping is okay, and so the compiler can eliminate the error path. This is an example of language semantics dictating particular implementation: if I require overflow checks, I am going to get more code, because I have to perform the check. If I do not require the checks, I get less code, because I do not perform the checks. (Where this gets more interesting is in larger examples where the checks get elided because the compiler can prove they aren't necessary, but this is already a tangent of a tangent.)In Ruby, there are no free functions. If I write a similar add function:
def add(x, y)
x + y
end
This function is not a free function: it is a new private method on the Object class. When you invoke a function in Ruby, it's not like Rust, where you simply find the function with the name you're invoking, and then call it. You instead perform "method lookup," which has some details I will elide, but for the purposes of this discussion, the idea is that you first look at the receiver to see if it has the add method defined, and then if it does not, you look at the receivers' parent class, and if it's not there, you keep going until you hit the top of the hierarchy. Once the method definition is found, you then invoke it.Now, it's not as if Rust doesn't also have method lookup (though the algorithm is entirely different), but Rust's design means that method lookup (in the vast majority of cases) is a compile-time thing: the lookup happens while you're building the software, and then at runtime, it simply calls the function that you found.
So why can't Ruby run method lookup at compile time? Well, for one, I left out an important second step: Ruby provides a method called method_missing, as a metaprogramming tool. What this means is, if we look the whole way up the object hierarchy and do not find a method named add, we will then re-traverse the entire ancestor tree again, instead invoking each class's method_missing method on the way. method missing takes the name of the method that was trying to be called, the arguments to it, and any block passed to it, and you can then do stuff to figure out if you want to handle this. This means that, even if no add function is defined, it still may be possible for the call to succeed, thanks to a method_missing handler.
Okay well why can't we do that at compile time? Well, Ruby also lets you redefine functions at runtime at basically any time. The define_method method can be called and generate a method on anything, anywhere you want, for whatever reason. You could do this based on user input, even! And yes, that would be a terrible idea, and you probably shouldn't do it, but the implementation of the language requires at least some sort of runtime computation to pull this off in the general case.
Now, I also want to point out that in my understanding, there's caching on method lookup, so that can help reduce the cost in many scenarios. But the point stands that the language has features that Rust does not, and those features mean that certain things must be more expensive than languages that do not have those features.
> can we can imagine an alternate universe Python that's as close as possible to our Python, except really fast? What would be different?
We could, but you lose compatibility with most Python code, and so you're effectively creating a new language. People do try this though, Mojo being an example of this very recently. I am excited to see how it goes.
Re: How many lines of C it takes to execute a + b in Python
#148Earlier 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?