Live data from Hacker News

Python performance myths and fairy tales

lwn.net

141–150 of 221 posts

Re: Python performance myths and fairy tales

#141
post #131
post #48

Earlier quoted context omitted.

That makes it so that in absolute terms, Python is not as slow as you might naively expect. But we don't measure programming language performance in absolute terms. We measure them in relative terms, generally against C. And while your Python code is speculating about how this Python object will be unboxed, where its methods are, how to unbox its parameters, what methods will be called on those, etc., compiled code i…

Welp there is Mojo so looks like soon you will not really need to care that much. Prob will get better performance than C too.

Mojo feels less like a real programming language for humans and primarily a language for AI's. The docs for the language immediately dive into chatbots and AI prompts.

Re: Python performance myths and fairy tales

#142

Earlier quoted context omitted.

The 200-100 times slower is a bit cherry picked, but use case does matter. Typically from a user perspective, the initial starting time is either manageable or imperceptible in the cases of long running services, although there are other costs. If you look at examples that make the above claim, they are almost always tiny toy programs where the cost of producing byte/machine code isn't easily amortized. This quote fr…

I'm not so much cherry picking as I am specifically talking compute (not I/O,stdlib) performance. However, when measured for general purpose tasks, that would involve compute and things like I/O, stdlib performance, etc., Python on the whole is typically NOT 20-100x times slower for a given task. Its I/O layer is written in C like many other languages, so the moment you are waiting on I/O you have leveled the playing…

Even with compute performance it is probably closer than you expect.

Python isn't evaluated line-by-line, even in micropython, which is about the only common implementation that doesn't work in the same way.

Cython VM will produce an AST of opcodes, and binary operations just end up popping off a stack, or you can hit like pypy.

How efficiently you can keep the pipeline fed is more critical than computation costs.

     int a = 5;
     int b = 10;
     int sum = a + b;
Is compiled to:

     MOV EAX, 5
     MOV EBX, 10
     ADD EAX, EBX
     MOV [sum_variable]
In the PVM binary operations remove the top of the stack (TOS) and the second top-most stack item (TOS1) from the stack. They perform the operation, and put the result back on the stack.

That pop, pop isn't much more expensive on modern CPUs and some C compilers will use a stack depending on many factors. And even in C you have to use structs of arrays etc... depending on the use case. Stalled pipelines and fetching due to the costs is the huge difference.

It is the setup costs, GC, GIL etc... that makes python slower in many cases.

While I am not suggesting it is as slow as python, Java is also byte code, and often it's assumptions and design decisions are even better or at least nearly equal to C in the general case unless you highly optimize.

But the actual equivalent computations are almost identical, optimizations that the compilers make differ.

Re: Python performance myths and fairy tales

#144

> His "sad truth" conclusion is that "Python cannot be super-fast" without breaking compatibility. A decent case of Python 4.0? > So, maybe, "a JIT compiler can solve all of your problems"; they can go a long way toward making Python, or any dynamic language, faster, Cuni said. But that leads to "a more subtle problem". He put up a slide with a trilemma triangle: a dynamic language, speed, or a simple implementation.…

If Julia fixes it package manager problems (does it still take a while to load imports?), I think it could become popular.

Re: Python performance myths and fairy tales

#145
post #32

"Rewrite the hot path in C/C++" is also a landmine because how inefficient the boundary crossing is. so you really need "dispatch as much as possible at once" instead of continuously calling the native code

And it's not just inefficiency. Even with fancy FFI generators like PyO3 or SWIG, adding FFI adds a ton of work, complexity, makes debugging harder, distribution harder, etc.

In my opinion in most cases where you might want to write a project in two languages with FFI, it's usually better not to and just use one language even if that language isn't optimal. In this case, just write the whole thing in C++ (or Rust).

There are some exceptions but generally FFI is a huge cost and Python doesn't bring enough to the table to justify its use if you are already using C++.

Re: Python performance myths and fairy tales

#146
post #47

Python as a language will likely never have a "fast" implementation and still be Python. It is way too dynamic to be predictable from the code alone or even an execution stream in a way that allows you to simplify the actual code that will be executed at runtime either through AOC or JIT. The language is itself is also quite large in terms of syntax and built-in capability at this point which makes new feature-conple…

Pypy is 10x faster and is compatible with most cpython code. IMHO it was a big mistake not to adopt JIT during the 2-to-3 transition.

Isn't there an incoming JIT in 3.14?

Re: Python performance myths and fairy tales

#147
post #136

Earlier quoted context omitted.

I guess you mean "N". 10 is a literal, not a name. The part "N cannot be assumed to be ten, because that could be changed elsewhere in the code" implies well enough that the change could be to a non-integer value. (For that matter, writing `N: int = 10` does nothing to fix that.)

No, I mean the literal. CPython is more flexible than it has any right to be, and you're free to edit the memory pointed to by the literal 10.

Care to show how you believe this can be achieved, from within Python?

Re: Python performance myths and fairy tales

#149
post #123
post #32

"Rewrite the hot path in C/C++" is also a landmine because how inefficient the boundary crossing is. so you really need "dispatch as much as possible at once" instead of continuously calling the native code

These days it's "rewrite in Rust". Typically Python is just the entry and exit point (with a little bit of massaging), right? And then the overwhelming majority of the business logic is done in Rust/C++/Fortran, no?

With computer vision you end up wanting to read and write to huge buffers that aren't practical to serialize and are difficult to share. And even allocating and freeing multi-megabyte framebuffers at 60 FPS can put a little strain on the allocator, so you want to reuse them, which means you have to think about memory safety.

That is probably why his demo was Sobel edge detection with Numpy. Sobel can run fast enough at standard resolution on a CPU, but once that huge buffer needs to be read or written outside of your fast language, things will get tricky.

This also comes up in Tauri, since you have to bridge between Rust and JS. I'm not sure if Electron apps have the same problem or not.

Re: Python performance myths and fairy tales

#150
post #136

Earlier quoted context omitted.

No, I mean the literal. CPython is more flexible than it has any right to be, and you're free to edit the memory pointed to by the literal 10.

Care to show how you believe this can be achieved, from within Python?

  import ctypes

  ten = 10
  addr = id(ten)
  
  class PyLongObject(ctypes.Structure):
      _fields_ = [
          ("ob_refcnt", ctypes.c_ssize_t),
          ("ob_type", ctypes.c_void_p),
          ("ob_size", ctypes.c_ssize_t),
          ("ob_digit", ctypes.c_uint32 * 1),
      ]
  long_obj = PyLongObject.from_address(addr)
  
  long_obj.ob_digit[0] = 3
  assert 10 == 3
  
  # using an auxiliary variable to prevent any inlining
  # done at the interpreter level before actually querying
  # the value of the literal `10`
  x = 3
  assert 10 * x == 9
  assert 10 + x == 6
Post reply on HN